Skip to content

枚举和注解

对于特有的季节对象,季节的值只有四个(春夏秋冬),这些值一般都是只读,且不需要修改的

枚举是一组常量的集合,属于一种特殊的类,里面只包含一组有限的特点对象

自定义枚举类

  • 不需要提供setXxx方法,因为枚举对象值通常为只读,不能在外界增加或者减小,并不提供修改
  • 自定义枚举类的构造器是私有化的
  • 对枚举对象/属性使用final+static共同修饰,实现底层优化
  • 枚举对象名通常使用全部大写,即常量命名的规范
  • 枚举对象根据需要,也可以有多个属性
java
public class Enumeration {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN);    // 直接使用类
    }
}

// 自定义枚举类
class Season {
    private String name;
    private String desc;
    
    // 在Season内部,直接创建所有的固定对象
    public static final Season SPRING = new Season("春天", "温暖");
    public static final Season SUMMER = new Season("夏天", "炎热");
    public static final Season AUTUMN = new Season("秋天", "凉爽");
    public static final Season WINTER = new Season("冬天", "寒冷");
    
    // 构造器私有化,在外界就不可以new一个对象了,在类的内部还是可以使用的
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
    // 只留下get相关的方法,去掉set相关的方法,防止属性被修改
    public String getName() {
        return name;
    }
    public String getDesc() {
        return desc;
    }
    
    // 打印对象的信息
    @Override
    public String toString() {
        return "Season{" + "name='" + name + '\'' +
                ", desc='" + desc + '\'' + '}';
    }
}

关键字实现枚举类

使用enum关键字实现枚举类

java
public class Enumeration {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN);    // 直接使用类
    }
}

// 使用关键字实现枚举类,使用enum关键字替代class
enum Season {
    // 在Season内部,直接创建所有的固定对象,通过常量名(实参列表)  进行简化  实参列表和构造器关联
    // 使用枚举的时候,要求将定义的常量对象写在类的最前面,否则会报错
    SPRING("春天", "温暖"), SUMMER("夏天", "炎热"), AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷");     // 多个常量对象,中间使用逗号间隔
    
    private String name;
    private String desc;
    
    // 构造器私有化,在外界就不可以new一个对象了,在类的内部还是可以使用的
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
    // 只留下get相关的方法,去掉set相关的方法,防止属性被修改
    public String getName() {
        return name;
    }
    public String getDesc() {
        return desc;
    }
    
    // 打印对象的信息
    @Override
    public String toString() {
        return "Season{" + "name='" + name + '\'' +
                ", desc='" + desc + '\'' + '}';
    }
}

注意事项:

  • 当我们使用enum关键字开发一个枚举类时,默认会继承Enum类,而且是一个final
  • 传统的public static final Season SPRING = new Season("春天", "温暖"); 对象创建简化为SPRING("春天", "温暖");,这里需要知道,其调用哪个构造器
  • 如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
  • 当有多个枚举对象时,使用逗号间隔,最后一个用分号结尾
  • 枚举对象必须放在枚举类的首行

枚举常用的方法

使用enum关键字时,会隐式的继承Enum类,我们可以使用Enum类的相关方法,常用的方法有:

方法名描述
valueOf传递枚举类型的Class对象和枚举常量名称给静态方法valusOf,会得到与参数匹配的枚举常量
toString得到当前枚举常量的名称。可以通过重写这个方法,来使得到的结果更易读
equals在枚举类型中可以直接使用==来比较两个枚举类常量是否相等。Enum提供的这个equals()方法,也是可以直接使用==实现的。它的存在是为了在SetListMap中使用。注意,equals()是不可变的
hashCodeEnum实现了hashCode()来和equals()保持一致,它也是不可变的
name得到当前枚举常量的名称,但是更建议使用toString方法
ordinal得到当前枚举常量的次序
compareTo枚举类型实现了Comparaable接口,这样可以比较两个枚举常量的大小/编号(按照声明的顺序排列)
clone枚举类型不能被Clone,为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupportedException异常的不变Clone()

各种方法的使用演示:

java
    public static void main(String[] args) {
        Season autumn = Season.AUTUMN;
        // 演示name方法
        System.out.println(autumn.name());    // AUTUMN   输出枚举对象的名称
        
        // 演示ordinal方法
        System.out.println(autumn.ordinal());    // 2   输出枚举对象的次序/编号,从0开始编号
        
        // values方法,返回Season数组,包含定义的所有枚举对象,可以从反编译找到values方法
        Season[] values = Season.values();
        for(Season season: values) {  // 增强for循环,依次从values数组中取出数据,赋值给season
            System.out.println(season);   // 输出四个枚举对象
        }
        
        // 演示valueOf方法:将字符串转换成枚举对象,要求字符串必须为已有的常量,否则报错
        // 执行流程:1.根据给定的字符串到Season的枚举类对象中去查找
        // 2.如果找到了,就返回,如果没有找到,就报错
        Season autumn1 = Season.valueOf("AUTUMN");
        System.out.println(autumn1);   // 这里的autumn1和之前的autumn指向的是同一个对象
        
        // 演示compareTo方法
        // 将前后两个对象的枚举编号进行比较
        // 结果返回前面的编号减去后面的编号  Season.AUTUMN的编号 - Season.SUMMER的编号
        System.out.println(Season.AUTUMN.compareTo(Season.SUMMER));   // 1
    }
}

// 使用关键字实现枚举类,使用enum关键字替代class
enum Season {
    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 + '\'' + '}';
    }
}

enum实现接口

使用enum关键字后,就不能再继承其他类了,因为enum会隐式的继承Enum类,而Java是但继承机制的

枚举类和普通类一样,可以实现接口:enum 类名 implements 接口1, 接口2 {}

java
interface IPlaying {
    public void playing();
}

enum Music implements IPlaying {
    CLASSICMUISC;
    @Override
    public void playing() {
        System.out.println("播放音乐");
    }
}

// 主函数调用
Music.CLASSICMUISC.playing();   // 播放音乐

注解

注解也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息

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

使用注解时,需要在其前面加上@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素

@Override

@Override注解用于限定某个方法,是重写父类方法,该注解只能用于方法(不能修饰其他类、包、属性等)

java
class Father {  // 父类
    public void fly() {
        System.out.println("Father fly...");
    }
}

class Son extends Father {   // 子类
    @Override
    // @Override注解放在fly方法上,表示子类的fly重写了父类的fly方法
    // 但是这里如果没有写@Override,还是对父类的fly方法进行重写
    // @Override注解的价值是做了语法的校验:如果写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有重写,则编译错误
    public void fly() {
        System.out.println("Son fly...");
    }
    
    @Override    // 报错,父类没有say方法,不构成重写
    public void say() {
        System.out.println("Son fly...");
    }
}

对于@Override的源码定义:

java
@Target(ElementType.METHOD)    // @Target表示注解可以放在哪些元素上,ElementType.METHOD修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override {   
}

@interface表示的是Override是一个注解,@interfacejdk5.0之后加入

@Target是修饰注解的注解,称之为元注解

@Deprecated

@Deprecated注解用于表示某个程序元素(类、方法、字段、包和参数等)已过时(过时不能代表不能使用,被修饰已过时的元素,还是可以使用的)

@Deprecated可以做版本升级过渡使用

java
@Deprecated
// @Deprecated修饰某个元素,表示该元素已经过时
// 对于过时的元素,即不在推荐使用,但是仍然可以使用,使用过时元素,其元素中间会出现一条划线
class A {
    public int n1 = 10;
    @Deprecated
    public void hi() {}
}

对于@Deprecated的源码定义:

java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {   
}

@Target中的内容依次是:构造器、属性字段、局部变量、方法、包、参数和类(类型)

@SuppressWarnings

@SuppressWarnings注解用于抑制编译器警告

代码编译器中,有时会出现奇怪的警告,如果我们不想让其出现编译器警告,我们可以使用@SuppressWarnings注解进行注释,让警告信息不显示

@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抑制与未用的程式码及停用的程式码相关的警告
java
public class SuppressWarnings {
    // @SuppressWarnings({"all"})   // 使用all一劳永逸
    @SuppressWarnings({"rawtypes", "unchecked", "unused"})   // 精准抑制
    public static void main(String[] args) {
        List list = new ArrayList();  // 可以使用rawtypes抑制
        list.add("a");   // 可以使用unchecked抑制
        list.add("b");   // 可以使用unchecked抑制
        list.add("c");   // 可以使用unchecked抑制
        int i;   // 可以使用unused抑制
        System.out.println(list.get(1));
    }
}

我们可以在编辑器右边滚动条中,通过鼠标悬停查看具体的警告类型,再精准注释即可

关于SuppressWarnings作用范围,是和放置的位置相关,上述注解放置在main方法上,那么抑制警告的范围就是main方法,只管理当前的代码块,其他方法就不受抑制注解的影响,通常我们将抑制注解语句放置在语句,方法或者类上

对于@SuppressWarnings的源码定义:

java
@Target({TYPE, FIELD, METHOD, PACKAGE, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings {  
    string[] value();
}

该注解类有一个数组,数组可以接收传入的内容

元注解

修饰注解的注解叫做元注解,在源码中会出现,常见的元注解有:

  • Retention:指定注解的作用范围,值有三种:SOURCECLASSRUNTIME (源码级别,类,运行时)

    只能用于修饰一个注解定义,用于指定该注解可以保留多长时间

    有三种策略值:保留策略从上到下越来越长

    • RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注释
    • RetentionPolicy.RUNTIME:编译器将把注解记录在class文件中,当运行Java程序时,JVM不会保留注解,这个策略值是默认值
    • RetentionPolicy.SOURCE:编译器将把注解记录在class文件中,当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注释
  • Target:指定注解可以在哪些地方使用

    具体的值为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE

    构造器、属性字段、局部变量、方法、包、参数和类(类型)

  • Documented:指定该注解是否在javadoc体现,即在生成文档时,可以看到该注解

  • Inherited:子类会继承父类注解

    Inherited修饰的注解将具有继承性,如果某个类使用了该元注解进行修饰,则其子类将自动具有该注解

Released under the MIT License.