• 9. Java枚举和注解


    1. 枚举

    1.1 为什么需要枚举

    看一个例子:

    public class Test {
    	public static void main(String[] args) {
    		 Season spring = new Season("春天", "温暖");
    		 Season summer = new Season("夏天", "炎热");
    		 Season autumn = new Season("秋天", "凉爽");
    		 Season winter = new Season("冬天", "寒冷");
    		 autumn.setName("XXX");
    		 autumn.setDesc("非常的热..");
    		 Season other = new Season("红天", "~~~");
    	}
    }
    class Season{
    	 private String name;
    	 private String desc;//描述
    	 public Season(String name, String desc) {
    		 this.name = name;
    		 this.desc = desc;
    	 }
    	 public String getName() {
    		return name;
    	 }
    	 public void setName(String name) {
    		this.name = name;
    	 }
    	 public String getDesc() {
    		return desc;
    	 }
    	 public void setDesc(String desc) {
    		this.desc = desc;
    	 }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    季节类有如下特点:
    (1)它的对象是固定的4个对象:spring, summer, autumn, winter。
    (2)对象的属性是只读,不需要修改的。
    按这个设计类的思路,不能体现季节类的上述特点。因此,需要有更好的设计类的方式—— 枚举。

    1.2 枚举(enumeration)介绍

    枚举(enumeration,简写 enum):是一组常量的集合。
    可以这样理解:枚举属于一种特殊的类,里面只包含有限个特定的对象。

    枚举的 2 种实现方式:
    (1)自定义类实现枚举。
    (2)使用 enum 关键字实现枚举。

    1.3 自定义类实现枚举

    对 9.1 例子的优化方法 1:

    public class Test {
    	public static void main(String[] args) {
    		 System.out.println(Season.SPRING);
    		 System.out.println(Season.WINTER);
    	}
    }
    class Season{
    	//枚举对象根据需要可以有多个属性
    	private String name;
    	private String desc;//描述
    	//1. 将构造器私有化,防止在其他类中直接new
    	//2. 去掉set方法, 防止属性被修改
    	//3. 在Season内部,直接创建固定的对象,对象名大写(命名规范)
    	//4. static修饰,在其他类中不创建对象,直接访问
    	//5. final修饰,常量对象
    	public final static Season SPRING = new Season("春天", "温暖");
    	public final static Season SUMMER = new Season("夏天", "炎热");
    	public final static Season AUTUMN = new Season("秋天", "凉爽");
    	public final static Season WINTER = new Season("冬天", "寒冷");
    	private Season(String name, String desc) {
    		this.name = name;
    		this.desc = desc;
    	}
    	public String getName() {
    		return name;
    	}
    	public String getDesc() {
    		return desc;
    	}
    	@Override
    	public String toString() {
    		return "Season{" +
    			"name='" + name + '\'' +
    			", desc='" + desc + '\'' +
    			'}';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    输出结果:

    Season{name='春天', desc='温暖'}
    Season{name='冬天', desc='寒冷'}
    
    • 1
    • 2

    自定义类实现枚举要点:
    (1)构造器私有化,防止在其他类中直接new。
    (2)可以提供 get 方法,但不提供 set 方法,因为属性只读。
    (3)本类内部创建一组固定的对象(如上面的:春夏秋冬),对象名大写。
    (4)对象用 public final static 修饰。(static:其他类中不创建对象即可访问,final:常量)

    【注】被 final 修饰的对象引用,指向的对象不能改变,对象的属性可以改变(若允许访问 set 方法)。

    public class Test {
        public static void main(String[] args) {
            System.out.println(AA.aa);
            AA.aa.setName("tom");
            System.out.println(AA.aa);
            //AA.aa = new AA("jerry");//错误
        }
    }
    class AA{
        private String name;
        public static final AA aa = new AA("jack");
        public AA(String name) {
            this.name = name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "AA{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    输出结果:

    AA{name='jack'}
    AA{name='tom'}
    
    • 1
    • 2

    1.4 enum 关键字实现枚举

    对 9.1 例子的优化方法 2:

    public class Test {
    	public static void main(String[] args) {
    		 System.out.println(Season.SPRING);
    		 System.out.println(Season.WINTER);
    	}
    }
    enum Season{
    	//1. 使用关键字enum替代class
    	//2. SPRING("春天", "温暖") <==> public static final Season SPRING = new Season("春天", "温暖")
    	//3. 如果有多个常量(对象),使用“,”号间隔
    	//4. 多个常量(对象)一起写在前面,可一行或多行
    	//5. 如果使用的是无参构造器,创建常量对象时可以省略 ()
    	SPRING("春天", "温暖"), //参数传给构造器,对象名为SPRING
    	SUMMER("夏天", "炎热"), 
    	AUTUMN("秋天", "凉爽"), 
    	WINTER("冬天", "寒冷");
    	private String name;
    	private String desc;//描述
    	private Season(String name, String desc) {
    		this.name = name;
    		this.desc = desc;
    	}
    	public String getName() {
    		return name;
    	}
    	public String getDesc() {
    		return desc;
    	}
    	@Override
    	public String toString() {
    		return "Season{" +
    			"name='" + name + '\'' +
    			", desc='" + desc + '\'' +
    			'}';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    enum 关键字实现枚举要点:
    (1)使用关键字 enum 替代 class。
    (2)构造器私有化,防止在其他类中直接new。
    (3)可以提供 get 方法,但不提供 set 方法,因为属性只读。
    (4)当有多个枚举对象时,多个枚举对象一起写在前面,可一行或多行;枚举对象之间使用 ”,“ 间隔,最后有一个分号结尾。
    (5)如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略。
     (6)使用 enum 关键字开发一个枚举类时,默认继承 Enum 类,而且开发的枚举类是一个 final 类。可以用 javap工具来验证。
    在这里插入图片描述
    【例1】下面代码是否正确, 并说明表示的含义?

    enum Gender{ 
    	BOY, GIRL;
    }
    
    • 1
    • 2
    • 3

    上面语法正确。
    有一个枚举类 Gender, 没有属性;有两个枚举对象 BOY、GIRL,使用无参构造器创建。
    如果有其他构造器,必须写上无参构造器,因为有了其他构造器,系统就不再添加默认的无参构造器:

    enum Gender{ 
    	BOY, GIRL; 
    	private Gender(String name){};
    	private Gender(){};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    【例2】下面代码输出什么?

    public class Test {
    	public static void main(String[] args) {
    		Gender2 boy = Gender2.BOY;
    		Gender2 boy2 = Gender2.BOY;
    		System.out.println(boy);//输出BOY//本质就是调用Gender2的父类Enum的toString
    		System.out.println(boy2 == boy);//true
    	}
    }
    enum Gender2{//父类Enum的toString 
    	BOY, GIRL;
    	//public static final Gender2 BOY = new Gender2();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行 System.out.println(boy) 时,自动调用 toString 方法,Gender2 类没有,就调用父类 Enum 的 toString 方法。Enum 的 toString 方法如下:
    在这里插入图片描述
    所以,Enum 的 toString 方法返回枚举常量名(即对象名),执行 System.out.println(boy) 输出 boy 对应的 Gender2 中的常量对象名 “BOY”。
    至于 boy2 == boy,因为两者指向同一个对象,所以为true。

    输出结果:

    BOY
    true
    
    • 1
    • 2

    1.5 enum 成员方法

    使用关键字 enum 时,会隐式继承 Enum 类,这样就可以使用 Enum 类相关的方法。Enum 类源码定义为:

    public abstract class Enum<E extends Enum<E>>
    	 implements Comparable<E>, Serializable {
    }
    
    • 1
    • 2
    • 3

    Enum 类中的构造方法:

    • toString:Enum 类重写了 Object 类的 toString 方法,返回当前的枚举对象名;子类还可以再重写该方法,用于返回对象的属性信息。
    • name:返回当前枚举对象名,子类不能重写该方法。
    • ordinary:返回当前对象编号,默认从 0 开始。
    • values:返回当前枚举类中的所有常量对象。
    • valueOf:将字符串转换成枚举对象,去枚举类中查找该对象并返回。要求字符串必须为枚举类中的常量对象名,否则报异常。
    • compareTo:比较两个枚举常量,返回序号之差。

    Enum 类中有唯 一 一 个构造方法:
    程序员不能调用此构造方法。它供编译器响应枚举类型时使用。枚举类在创建常量对象的时候自动调用了父类 Enum 的构造方法,自己的名字和编号传给父类的属性。

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
    
    • 1
    • 2
    • 3
    • 4
    public class EnumMethod {
        public static void main(String[] args) {
            Season autumn = Season.AUTUMN;
            //枚举对象的名字
            System.out.println(autumn.name());
            //枚举对象的次序/编号
            System.out.println(autumn.ordinal());
            //获取枚举类中的所有枚举对象
            //values方法 ctrl+b定位不到,class文件时反编译可以看到
            Season[] values = Season.values();
            for (Season season : values) {//增强for循环
                System.out.println(season);
            }
            //根据参数“AUTUMN”到Season的枚举对象中去查找,如果找到了就返回;如果没找到就报错。
            Season autumn1 = Season.valueOf("AUTUMN");
            System.out.println(autumn1);
            System.out.println(autumn == autumn1);
            //Season.SPRING的编号[0] - Season.WINTER的编号[3]
            System.out.println(Season.SPRING.compareTo(Season.WINTER));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    输出结果:

    AUTUMN
    2
    Season{name='春天', desc='温暖'}
    Season{name='夏天', desc='炎热'}
    Season{name='秋天', desc='凉爽'}
    Season{name='冬天', desc='寒冷'}
    Season{name='秋天', desc='凉爽'}
    true
    -3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    【例】声明 Week 枚举类,其中包含星期一至星期日的定义:MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY。使用values返回所有的枚举数组,并遍历,输出如下信息。

    星期一
    星期二
    星期三
    星期四
    星期五
    星期六
    星期日

    package enum_.week;
    public class TestWeek {
        public static void main(String[] args) {
            Week[] weeks = Week.values();
            for (Week week : weeks) {
                System.out.println(week);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    package enum_.week;
    public enum Week {
        MONDAY("星期一"),
        TUESDAY("星期二"),
        WEDNESDAY("星期三"),
        THURSDAY("星期四"),
        FRIDAY("星期五"),
        SATURDAY("星期六"),
        SUNDAY("星期日");
    
        private String name;
        private Week(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    输出结果:

    星期一
    星期二
    星期三
    星期四
    星期五
    星期六
    星期日
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.6 枚举类使用细节

    • 枚举类不能再继承其他类,因为会隐式继承 Enum 类,而 Java 是单继承机制。
    • 枚举类和普通类一样,可以实现接口。
    package enum_;
    public class TestMusic {
        public static void main(String[] args) {
            Music.CLASSIC_MUSIC.play();
        }
    }
    interface IPlay {
        void play();
    }
    enum Music implements IPlay{
        CLASSIC_MUSIC;
        @Override
        public void play() {
            System.out.println("正在播放古典音乐...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果:

    正在播放古典音乐...
    
    • 1

    2. 注解

    2.1 基本介绍

    • 注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。

    • 和注释一样,注解不影响程序逻辑,但注解可以被编译运行,相当于嵌入在代码中的补充信息。

    • 在 JavaSE 中,注解的使用目的比较简单,例如:标记过时的功能,忽略警告等。在 JavaEE 中,注释占据了更重要的角色,了例如用来配置应用程序的任何切面,代替 JavaEE 旧版中所遗留的繁冗代码和 XML 配置等。

    • 使用注解时,要在前面加上@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。
      三个基本的注解:
      @Override:表示某个方法是重写了父类的方法。
      @Deprecated:表示某个程序元素(类、方法等)已经过时。
      @SuppressWarnings:抑制编译器警告。

    2.2 @Override 注解

    • @Override 注解,表示子类的方法重写了父类的方法。
    • 如果代码上构成重写但没有 ”@Override“,仍是重写。如果有 ”@Override“,编译器就检查是否真的重写了父类方法,若不构成重写,则编译错误。
    • 下面是 ”@Override“ 的定义(@interface表示一个注解类)
       @Target(ElementType.METHOD)//@Override只能修饰方法
       @Retention(RetentionPolicy.SOURCE)
       public @interface Override {
       }
      
      • 1
      • 2
      • 3
      • 4
      @Override 只能修饰方法,不能修饰类、包、属性等,从 @Target 注解以可看出。
      @Target 注解是修饰注解的注解,称为元注解。
    package annotation_;
    public class Override_ {
        public static void main(String[] args) {
        }
    }
    class Father{//父类
        public void fly(){
            System.out.println("Father fly...");
        }
    }
    class Son extends Father {//子类
        //1.@Override注解,表示子类的fly方法重写了父类的fly
        //2.这里如果没有@Override,仍是重写
        //3.如果有,编译器就检查是否真的重写了父类方法,若不构成重写,则编译错误
        //4.下面是@Override的定义(@interface表示一个注解类)
        //@Target(ElementType.METHOD)//只能修饰方法
        //@Retention(RetentionPolicy.SOURCE)
        //pub
        lic @interface Override {
        //}
        @Override 
        public void fly() {
            System.out.println("Son fly....");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.3 @Deprecated 注解

    • @Deprecated 用于表示某个程序元素(类, 方法等)已过时,即不再推荐使用,但是仍然可以使用。
    • @Deprecated 注解类的源码:
      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
      public @interface Deprecated {
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      @Deprecated 注解可以修饰方法、类、字段、包、参数等等。
      @Deprecated 的作用:新旧版本的兼容和过渡。
    • 在 idea 中,使用 @Deprecated 修饰的元素时会有一个中划线。

    2.4 @SuppressWarnings 注解

    • 不希望看到警告时,可以使用 @SuppressWarnings 注解来抑制警告信息。
    • 在{“”} 中,可以写入希望抑制(不显示)的警告类型(各种警告类型见本节末尾)。
      如下图,在 idea 中点击右侧黄线,可得到警告类型,从而设置{“”} 中 参数。
      请添加图片描述
    • @SuppressWarnings 的作用范围与放置的位置相关,比如:放置在 main 方法,抑制警告的范围就是 main 方法。
    • @SuppressWarnings 源码:
      @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
      @Retention(RetentionPolicy.SOURCE)
      public @interface SuppressWarnings {
      	String[] value();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      @SuppressWarnings 注解通常放置在具体的类上,放在方法、语句上也可以。
      该注解类有数组 String[] values(),用于接收若干警告类型字符串,如 {“rawtypes”, “unchecked”, “unused”}

    @SuppressWarnings 注解中,可以指定的警告类型有:

    名称可以抑制的警告
    all抑制所有警告
    boxing抑制与封装/拆装作业相关的警告
    cast抑制与强制转型作业相关的警告
    dep-ann抑制与淘汰注释相关的警告
    deprecation抑制与淘汰的相关警告
    fallthrough抑制与 switch 陈述式中遗漏 break 相关的警告
    finally抑制与未传回 finally 区块相关的警告
    hiding抑制与隐藏变数的区域变数相关的警告
    incomplete-switch抑制与 switch 陈述式(enum case)中遗漏项目相关的警告
    javadoc抑制与 javadoc 相关的警告
    nls抑制与非 nls 字串文字相关的警告
    null抑制与空值分析相关的警告
    rawtypes抑制与使用 raw 类型相关的警告
    resource抑制与使用 Closeable 类型的资源相关的警告
    restriction抑制与使用不建议或禁止参照相关的警告
    serial抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告
    static-access抑制与静态存取不正确相关的警告
    static-method抑制与可能宣告为 static 的方法相关的警告
    super抑制与置换方法相关但不含 super 呼叫的警告
    synthetic-access抑制与内部类别的存取未最佳化相关的警告
    sync-override抑制因为置换同步方法而遗漏同步化的警告
    unchecked抑制与未检查的作业相关的警告
    unqualified-field-access抑制与栏位存取不合格相关的警告
    unused抑制与未用的程式码及停用的程式码相关的警告

    2.5 元注解

    JDK 的元注解用于修饰其他注解,本身作用不大,了解的目的是:看源码时,可以知道它是干什么的。

    元注解的种类 (了解即可):

    • Retention:指定注解的作用范围,三种:SOURCE(源码)、CLASS(类)、RUNTIME(运行时)。
    • Target:指定注解可以在哪些地方使用。
    • Documented:指定该注解是否会在 javadoc 体现(javadoc中是否会记录该注解)。
    • Inherited:子类会继承父类注解。
  • 相关阅读:
    (学习笔记)数仓建模
    【配置文件】Redis配置文件解读和持久化
    【Unity3D】素描特效
    1029 旧键盘
    redis-cli客户端中获取数据中文显示xe问题
    lambda表达式
    python利用ffmpeg实现视频流的下载,并分段保存
    PostgreSQL文本搜索(五)——词法分析器
    腰部外骨骼机器人线性自抗扰控制器参数优化
    SEGGER Embedded Studio IDE移植FreeRTOS
  • 原文地址:https://blog.csdn.net/qq_44378854/article/details/126127266