枚举和注解

发布时间 2023-03-26 11:11:01作者: 爱新觉罗LQ

枚举和注解

枚举

1. 枚举定义

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

2. 自定义实现枚举

创建 Season 对象有如下特点:

  • 季节的值是有限的几个值(spring,summer,autumn,winter)
  • 只读,不需要修改
//	创建 Season 对象
public class Enumeration01 {
    public static void main(String[] args) {
        //  使用
        Season1 spring = new Season1("春天", "温暖");
        Season1 winter = new Season1("冬天", "寒冷");
        Season1 summer = new Season1("夏天", "炎热");
        Season1 autumn = new Season1("秋天", "凉爽");

        autumn.setName("XXX");
        autumn.setDesc("非常的热...")

        //  对于季节而言,它的对象(具体值)是固定的4个,不会有更多
        //  按照老师设计的这个类的思路,不能体现季节是固定的4个对象
        //  因此,这样的设计不好====> 枚举类【枚:一个一个,举:例举,即把具体的对象一个一个列举出来的类
        //  就称为枚举类】
        Season1 other = new Season1("嘿天", "~~~");
    }
}

class Season1 {   // 类
    private String name;
    private String desc;    //  描述

    public Season1(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;
    }
}

优化并修改:

  • 将构造器私有化,目的防止:直接 new
  • 去掉 setXxx 方法,防止属性被修改
  • 在Season 内部,直接创建固定的对象
  • 对外暴露对象(通过为对象添加 public static final 修饰符)
public class Enumeration02 {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN);
        System.out.println(Season.SPRING);
    }
}


//  演示自定义枚举实现
class Season{   // 类
    private String name;
    private String desc;    //  描述

    //  定义了4个对象, 固定
    //  为什么是 static,因为希望它可以直接被访问
    public static final Season SPRING = new Season("春天", "温暖");
    public static final Season WINTER = new Season("冬天", "寒冷");
    public static final Season AUTUMN = new Season("秋天", "凉爽");
    public static final Season SUMMER = new Season("夏天", "炎热");

    //  1)将构造器私有化,目的:防止 直接new
    //  2)去掉setXxx方法 【只能读,不能改】,防止属性被修改
    //  3)在Season 内部,直接创建固定的对象
    //  4)优化,可以加入 final 修饰符 【final 和 static 往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理】
    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 + '\'' +
            '}';
    }

3. enum 枚举类

使用 enum 来实现枚举类

  • 使用关键字 enum 替代 class
  • public static final Season SPRING = new Season("春天", "温暖") 直接使用 SPRING("春天", "温暖") 即:对象名(实参列表)
  • 如果有多个常量(对象),使用 逗号 间隔即可
  • 如果使用 enum 来实现枚举,要求将定义的常量对象,写在前面
enum Season2 {   // 类

    //  如果使用了 enum 来实现枚举类
    //  1)使用关键字 enum 替代 class
    //  2)ublic static final Season SPRING = new Season("春天", "温暖");直接使用
    //  SPRING("春天", "温暖")解读  常量名(实参列表)
    //  3)如果有多个常量(对象),使用逗号间隔即可
    //  4)如果使用enum 来实现枚举,要求定义常量对象,写在前面

    SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热");

    private String name;
    private String desc;    //  描述

    private Season2(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 + '\'' +
            '}';
    }
}

4. 使用enum 实现枚举注意事项

  • 当我们用 enum 关键字开发一个枚举类时,默认会继承 Enum 类,而且是一个 final 类【使用 javap 工具来演示】

image-20221117164643972

image-20221117165737171

  • 传统的 public static final Season2 SPRING = new Season2("春天", "温暖"),简化成 SPRING("春天", "温暖"),这里必须知道,它调用的是哪个构造器
  • 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
  • 当有多个枚举对象时,使用 , 间隔,最后有一个分号结尾
  • 枚举对象必须放在枚举类的行首

看下 Enum 类的 toString() 方法

/**
     * Returns the name of this enum constant, as contained in the
     * declaration.  This method may be overridden, though it typically
     * isn't necessary or desirable.  An enum type should override this
     * method when a more "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
public String toString() {
    return name;	//	返回枚举常量的名字
}

5. Enum 常用方法

使用关键字 enum 时,会隐式继承 Enum 类,这样我们就可以使用 Enum 类相关方法

方法名 详情描述
valueOf 将字符串转换成枚举对象,要求字符串必须为已有常量名,否则报异常
values(通过反编译才能看到) 返回 Seasons[],含有定义的所有枚举对象
toString 默认返回枚举常量的名字
equals
hashCode
getDeclaringClass
name 输出枚举对象的名称
ordinal 该枚举对象的次序(编号:从 0 开始)
compareTo 比较2个枚举常量的 编号
clone
public final int compareTo(E o) {
    Enum<?> other = (Enum<?>)o;
    Enum<E> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;	//	编号1 - 编号2
}

练习:

1)声明Week 枚举类,其中包含 星期一至星期日的定义:MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;

2)使用values 返回所有的枚举数组,并遍历

public class EnumExercise02 {

    public static void main(String[] args) {
        //  获取到所有的枚举对象, 即数组
        Week[] weeks = Week.values();	//	别忘记写 main 方法
        for (Week week : weeks) {
            System.out.println(week);
        }
    }
}
enum Week {
    //  定义Week的枚举对象
    MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),
    FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");
    private String name;

    private Week(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

6.Enum使用细节

  • 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制
  • 枚举类和普通类一样,可以实现接口,如下形式
enum 类名 implements 接口1, 接口2{}

注解

  • 注解(Annotatino)也称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息
  • 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息
  • 在 JavaSE 中,注解的使用目的比较简单,例如:
    • 标记过时的功能
    • 忽略警告等
  • 在 JavaEE 中,注解占据了更重要的角色,例如:
    • 配置应用程序的任何切面
    • 代替java EE旧版中所遗留的繁冗代码和XML配置等

1. 基本语法

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

2. 三个基本的 Annotation

  • @Override:限制某个方法,是重写父类的方法,该注解只能用于方法
    • 如果的确写了 @Override 注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过;如果没有构成重写,则编译错误【语法校验】
//	@Target 是修饰注解的注解,称为 元注解

@Target(ElementType.METHOD)	//	说明只能修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override {	//	@interface 不是 interface,是注解类。 jdk5.0 之后加入的
}
  • @Deprecated:用于表示某个程序元素(类,方法等)已过时
    • 可以做版本升级过渡使用
public class Deprecated_ {
    public static void main(String[] args) {
        A a = new A();
        a.hi();
        System.out.println(a.n1);
    }
}

//  老韩解读
//  1.  @Deprecated 修饰某个元素,表示该元素已经过时
//  2.  即不再推荐使用,但是仍然可以使用
@Deprecated
class A{
    @Deprecated
    public int n1 = 10;

    @Deprecated
    public void hi(){

    }
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
    /**
     * Returns the version in which the annotated element became deprecated.
     * The version string is in the same format and namespace as the value of
     * the {@code @since} javadoc tag. The default value is the empty
     * string.
     *
     * @return the version string
     * @since 9
     */
    String since() default "";

    /**
     * Indicates whether the annotated element is subject to removal in a
     * future version. The default value is {@code false}.
     *
     * @return whether the element is subject to removal
     * @since 9
     */
    boolean forRemoval() default false;
}
  • @SuppressWarnings:抑制编译器警告
@SuppressWarnings("all")
public class SuppressWarnings_ {

    //  老韩解读
    //  1) 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息
    //  2) 在{""}中,可以写入你希望抑制(不显示)警告信息
    //  3) 可以抑制的警告类型在如下文档
    //	4) 关于 SuppressWarnings 使用范围是和你放置的位置相关
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("mary");
        int i;
        System.out.println(list.get(1));
    }
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    /**
     * The set of warnings that are to be suppressed by the compiler in the
     * annotated element.  Duplicate names are permitted.  The second and
     * successive occurrences of a name are ignored.  The presence of
     * unrecognized warning names is <i>not</i> an error: Compilers must
     * ignore any warning names they do not recognize.  They are, however,
     * free to emit a warning if an annotation contains an unrecognized
     * warning name.
     *
     * <p> The string {@code "unchecked"} is used to suppress
     * unchecked warnings. Compiler vendors should document the
     * additional warning names they support in conjunction with this
     * annotation type. They are encouraged to cooperate to ensure
     * that the same names work across multiple compilers.
     * @return the set of warnings to be suppressed
     */
    String[] value();	//	设置一个数组,比如:{"rawtypes", "unchecked", "unused"}
}

@SuppressWarning 中的属性介绍以及属性说明

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

3. 四种元注解

JDK 的元 Annotation 用于 修饰其它 Annotation

  1. Retention(保留):指定注解作用范围

    • SOURCE【源码】
      • 编译器使用后,直接丢弃这种策略的注解
    • CLASS【类】
      • 编译器把注解记录在 class 文件中,当运行 Java 程序时,JVM不会保留注解。这是默认值
    • RUNTIME【运行时】
      • 编译器把注解记录在 class 文件中,当运行 Java 程序时,JVM会保留注解。程序可以通过反射获取该注解

    未命名绘图

    //	包含一个 RetentionPlicy 类型的成员变量
    //	使用 @Rentention 时,必须为该 value 成员变量指定值
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy(策略)
         */
        RetentionPolicy value();	
    }
    
  2. Target:指定注解可以在哪些地方使用

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }
    
    //	看下 ElementType 可以取哪些值
    
    
  3. Documented:指定该注解是否会在 javadoc 提取成文档,即:在生成文档时,可以看到该注解

    • 说明:定义为 Documented 的注解必须设置 Retention 值为 RUNTIME
    //	看下 Deprecated中有 @Decumented
    //	在某个方法上写了 Deprecated时,在生成 javadoc时,这个 Deprecated 会被保留在 javadoc 文档中
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
    public @interface Deprecated {	
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Documented {
    }
    
  4. Inherited:子类会继承父类注解