【JavaSE】12-枚举类与注解

十二、 枚举类与注解

12.1 枚举类的使用

其实就是项目三当中的员工状态,如FREE、BUSY、VACATION等状态类的对象。总的来说,当一个类只有有限个数的常量对象时,如季节、星期、性别、订单状态等,我们就优先考虑使用枚举类。

如果枚举类中只有一个对象,可以作为单例模式的实现方式。

12.1.1 自定义枚举类

JDK 5.0 之前自定义枚举类。

1.使用步骤

  • ① 声明枚举类对象的属性:private final;
  • ② 私有化枚举类的构造器;
  • ③ 提供枚举对象;
  • ④ 提供get()属性的方法;
  • ⑤ 重写toString方法。

2.例子:自定义枚举类Season

//自定义枚举类Season
class Season {
    //1.声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonDesc;


    //2.私有化类的构造器
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //3.枚举对象
    public static final Season SPRING = new Season("SPRING", "春天花开");//春
    public static final Season SUMMER = new Season("SUMMER", "夏日炎炎");//夏
    public static final Season AUTUMN = new Season("AUTUMN", "秋高气爽");//秋
    public static final Season WINTER = new Season("WINTER", "冰天雪地");//冬

    //4.提供get()属性的方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //5.重写toString方法
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

测试:

public class SeasonTest {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
    }
}

输出:

Season{seasonName='SPRING', seasonDesc='春天花开'}

12.1.2 关键字enum定义枚举类

JDK 5.0 之后使用enum关键字来定义枚举类。

1.使用步骤

① 把 class 改成 enum ;

② 把 "提供枚举对象"移到第一步;

③ 把 “相同的部分” 删掉,多个枚举对象用逗号分隔;

④ toString() 方法一般不重写。

2.例子:关键字enum定义枚举类Season1

//关键字enum定义枚举类Season1
enum Season1 {
    //1.提供枚举类的对象,多个对象之间用逗号隔开,末尾分号结束
    SPRING("SPRING", "春天花开"),
    SUMMER("SUMMER", "夏日炎炎"),
    AUTUMN("AUTUMN", "秋高气爽"),
    WINTER("WINTER", "冰天雪地");

    //2.声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonDesc;


    //3.私有化类的构造器
    private Season1(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //4.提供get()属性的方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }
}

测试:

public class SeasonTest1 {
    public static void main(String[] args) {
        Season1 summer = Season1.SUMMER;
        System.out.println(summer);

        //查看Season1的父类
        System.out.println(Season1.class.getSuperclass());
    }
}

输出:

SUMMER
class java.lang.Enum

可以看到,就算不重写 toString() 方法,也能正确输出枚举类。枚举类的父类是 java.lang.Enum。

12.1.3 Enum类常用方法

Enum类主要关注3个方法:

方法作用
values()返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf (String str )可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如果不是,会有运行时异常:IllegalArgumentException 。
toString()返回当前枚举类对象常量的名称
public class SeasonTest1 {
    public static void main(String[] args) {
        //values()方法的使用
        Season1[] season1s = Season1.values();//返回枚举类的对象数组
        System.out.println(Arrays.toString(season1s));

        //valueOf(String str)的使用
        String str = "WINTER";
        Season1 s = Season1.valueOf(str);
        System.out.println(s);

        //toString()方法的使用
        System.out.println(Season1.AUTUMN.toString());
    }
}

输出:

[SPRING, SUMMER, AUTUMN, WINTER]
WINTER
AUTUMN

12.1.4 实现接口的枚举类

情况1:实现接口,在enum类中实现抽象方法

//接口
interface info {
    void show();
}

//关键字enum定义枚举类Season1
enum Season1 implements info{
    //1.提供枚举类的对象,多个对象之间用逗号隔开,末尾分号结束
    SPRING("SPRING", "春天花开"),
    SUMMER("SUMMER", "夏日炎炎"),
    AUTUMN("AUTUMN", "秋高气爽"),
    WINTER("WINTER", "冰天雪地");

    //2.声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonDesc;


    //3.私有化类的构造器
    private Season1(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }


    //4.提供get()属性的方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //实现接口方法
    @Override
    public void show() {
        System.out.println("这是季节");
    }
}

测试:

public class SeasonTest1 {
    public static void main(String[] args) {
        Season1 summer = Season1.SUMMER;
        System.out.println(summer);
        //情况1:实现接口,在enum类中实现抽象方法
        summer.show();
    }
}

输出:

SUMMER
这是季节

情况2:让枚举类的对象分别实现接口中的抽象方法

//接口
interface info {
    void show();
}

//关键字enum定义枚举类Season1
enum Season1 implements info {
    //1.提供枚举类的对象,多个对象之间用逗号隔开,末尾分号结束
    SPRING("SPRING", "春天花开") {
        @Override
        public void show() {
            System.out.println("春天在哪里");
        }
    },
    SUMMER("SUMMER", "夏日炎炎") {
        @Override
        public void show() {
            System.out.println("夏天的风我永远记得");
        }
    },
    AUTUMN("AUTUMN", "秋高气爽") {
        @Override
        public void show() {
            System.out.println("秋天不回来");
        }
    },
    WINTER("WINTER", "冰天雪地") {
        @Override
        public void show() {
            System.out.println("大约在冬季");
        }
    };

    //2.声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonDesc;


    //3.私有化类的构造器
    private Season1(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }


    //4.提供get()属性的方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }
}

测试:

public class SeasonTest1 {
    public static void main(String[] args) {
        //values()方法的使用
        Season1[] season1s = Season1.values();//返回枚举类的对象数组
        System.out.println(Arrays.toString(season1s));
        //情况2:让枚举类的对象分别实现接口中的抽象方法
        for (int i = 0; i < season1s.length; i++) {
            season1s[i].show();
        }
    }
}

输出:

[SPRING, SUMMER, AUTUMN, WINTER]
春天在哪里
夏天的风我永远记得
秋天不回来
大约在冬季

12.2 注解的使用

12.2.1 注解(Annotation)介绍

  • 注解(Annotation) 是 JDK 5.0增加的新特性。
  • Annotation 其实就是代码里的特殊标记,这些标记可以在编译 、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
  • Annotation 可以像修饰符一样被使用 , 可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明 , 这些信息被保存在 Annotation的 “name=value” 对中。
  • 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE/Android 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 JavaEE 旧版中所遗留的繁冗代码和 XML 配置等。
  • 未来的开发模式都是基于注解的,JPA 是基于注解的, Spring2.5 以上都是基于注解的,Hibernate3.x 以后也是基于注解的,现在的 Struts2 有一部分也是基于注解的了,注解是一种趋势 ,一定程度上可以说: 框架 = 注解 + 反射 + 设计模式

12.2.2 常见的Annotation示例

1.文档注解

如下面所示,“@” 号后面所跟的就是文档注解。一般加在类或者方法前面,起到解释说明的作用。

/**
 * @Author: Sihang Xie
 * @Description: 
 * @Date: 2022/4/1 17:16
 * @param 
 * @return {{@link String}}
 */

2.JDK中内置的三个基本注解

注解描述
@Override限定重写父类方法,只能用于方法
@Deprecated表示所修饰的类、方法等已过时。通常是因为所修饰的结构危险或存在更好的选择
@SuppressWarnings抑制编译器警告,抑制变量未使用等警告

3.跟踪代码依赖性,实现替代配置文件功能

只需要在注解上声明,就不需要写冗长的XML文件了。

12.2.3 自定义Annotation

1.自定义的步骤

① 新建 Java Class的时候选择底部的 Annotation,如下图所示:

image-20220401195819431

② 声明 @interface 。注意,这个修饰符和接口 Interface 没有任何关系。

public @interface MyAnnotation {
}

③ 声明成员变量 String value。

public @interface MyAnnotation {
    String value();
}
  • 上面的 String value() 看起来像是方法,但其实是属性,只不过是以无参数方法的形式来声明。其方法名和返回值定义了该属性的名字和类型。我们称为配置参数。类型只能是八种基本数据类型:String类型、Class类型、enum类型、Annotation类型以及以上所有类型的数组。
  • 如果只有一个参数成员,建议使用参数名为value
  • 没有成员定义的 Annotation 称为标记,如 @Override。有些接口,如 Serializable ,其中没有定义任何抽象方法,我们称为标记接口

2.自定义注解中的赋值问题

@MyAnnotation(value = "Hello")
public void test(){
    System.out.println("test Annotation");
}

如上,@MyAnnotation() 中要赋值 “Hello” 才不会报错。若不想赋值也不报错,可以在自定义注解 @MyAnnotation 中添加 default 默认值,如下代码所示:

public @interface MyAnnotation {
    String value() default "Hello";
}
@MyAnnotation
public void test(){
    System.out.println("test Annotation");
}

但也可以不用默认值,可以自行更改。如下代码所示:

@MyAnnotation(value = "Hi")
public void test(){
    System.out.println("test Annotation");
}

上面自定义注解中的赋值其实没有任何意义。现在还不能很好理解自定义注解的作用没关系,这是要等学完反射和框架才能深入地理解到注解的应用场景。

12.2.4 JDK中的元注解

元注解是对现有的注解进行解释说明的注释。JDK 中一共有 4 个元注解。分别是:Retention、Target、Documented、Inherited。下面分别介绍这 4 个元注解。

以下是 @SuppressWarnings 的源代码,可以看到其上面添加了 2 个元注解 @Target 和 @Retention。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

1.Retention

声明了注解的 3 种生命周期:SOURCE、CLASS(默认)、RUNTIME。

状态描述
SOURCE编译的时候就被丢弃了
CLASS会被编译器保留在class文件当中,但执行的时候不会被加载到内存当中
RUNTIME会被编译器保留在class文件当中,而且执行的时候会被加载到内存当中

2.Target

用来声明自定义注解能够修饰哪些结构。以 @SuppressWarnings 为例:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
Target类型描述
TYPE能修饰Class、interface、enum
FIELD属性
METHOD方法
PARAMETER参数
CONSTRUCTOR构造器
LOCAL_VARIABLE局部变量
ANNOTATION_TYPE注解类型
PACKAGE

其中 @SuppressWarnings 只能修饰 Class、interface、enum、属性、方法、参数、构造器和局部变量。

  • 如果自定义注解,一般都会声明 Rentention 和 Target 两个元注解。其他两个则一般很少使用。

3.Documented

表示所修饰的注解在被 javadoc 解析时会被保留下来。

举个例子,就是 @Deprecated 会在形成 Java 文档时显示出来,如下图所示:

image-20220402085431361

4.Inherited

被它修饰的 Annotation 将具有继承性。如果某个类使用了被 @Inherited 修饰的 Annotation,则其子类将自动具有该注解。

  • 在实际发开中,应用较少。

12.2.5 利用反射获取注解信息(在反射部分涉及)

12.2.6 JDK 8中注解的新特性

JDK 8中注解有 2 个新特性:可重复注解类型注解

1.可重复注解

JDK 8 之前只能使用 Annotation[] 数组来写多个数组。如下代码所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotation {
    String value() default "Hello";
}
public @interface MyAnnotations {
    MyAnnotation[] value();//Annotation[] 数组
}
public class AnnotationTest {

    @MyAnnotations({@MyAnnotation(value = "Hello"), @MyAnnotation(value = "Hi")})//可重复注解
    public void test(){
        System.out.println("test Annotation");
    }
}

JDK 8 之后,只需声明 @Repeatable(MyAnnotations.class) 即可。注意:使用 @Repeatable 之后,@MyAnnotation 与 @MyAnnotations 的 @Retention 、 @Target 和 @Inherited等元注解信息要保持一致。否则就会报错。

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotation {
    String value() default "Hello";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {
    MyAnnotation[] value();
}
public class AnnotationTest {
    @MyAnnotation(value = "Hello")
    @MyAnnotation(value = "Hi")
    public void test(){
        System.out.println("test Annotation");
    }
}

2.类型注解

JDK 8 新增了 2 种类型注解:ElementType.TYPE_PARAMETER、ElementType.TYPE_USE。总的来说,可以让 Java 中可以添加注解的地方变得更多,让注解更加强大。

类型注解描述
ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中(如:泛型声明)
ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中

例子:

泛型,也是可以通过反射获取其注解。

① 自定义注解 @MyAnnotation 添加了TYPE_PARAMETER:

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})//末尾添加了TYPE_PARAMETER
public @interface MyAnnotation {
    String value() default "Hello";
}

② 测试:可以在泛型前添加自定义注解了

class Generic<@MyAnnotation T> {
}

自定义注解 @MyAnnotation 添加了TYPE_USE:

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER, TYPE_USE})末尾添加了TYPE_USE
public @interface MyAnnotation {
    String value() default "Hello";
}

测试:可以在好多你想不到的地方前添加注解。

public void show() throws @MyAnnotation RuntimeMBeanException {

    ArrayList<@MyAnnotation String> list = new ArrayList<>();

    int num = (@MyAnnotation int) 10L;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自牧君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值