上帝视角学JAVA- 基础13-枚举、注解【2021-08-18】

1、枚举类

JDK5.0之后,引入的专门的关键字 enum 定义枚举类

什么时候用枚举,同类事物可以有限穷举时。

比如小于 7的正整数: 1、2、3、4、5、6

人的时期划分:婴儿期、幼年期、青年期、中年期、老年期

订单的过程:新建订单、待付款、已付款、已发货、已确认、售后中、售后完成。

像上面这些情况就比较适合用枚举来定义。

定义一组常量,也适合使用枚举类。

1.1 JDK 5.0 之前定义枚举类

5.0之前都是自定义枚举类:

其实也是一个普通类,只不过对属性、构造器等做了要求。

public class Season {
    // 1、声明属性  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
    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("冬天", "冬天冬天");
​
    // 4、提供获取枚举类对象的属性的方法
    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
}

第一步定义 为final 是因为属性 枚举本身就是定义确定的,不可变的

第二步 私有化构造器,不希望外界来创建对象。这个枚举对象应该是我们造好的

第三步 定义多个公共的静态属性,值为该枚举类对象,使用私有化构造器初始化对象。

第四步 提供公共的属性获取方法,因为是属性是final的,没有setter方法。

1.2 JDK 5.0以后使用枚举类

1.2.1 使用enum定义枚举类

public enum SeasonEnum {
    // 1、提供当前枚举类常量,接收枚举类的对象,多个对象之间用对象隔开,最后一个使用分号结束。
    SPRING ("春天", "春天春天"),
    SUMMER ("夏天", "夏天夏天"),
    AUTUMN ("秋天", "秋天秋天"),
    WINTER ("冬天", "冬天冬天");
​
    // 2、声明属性  final 定义常量不可变
    private final String seasonName;
    private final String seasonDesc;
​
    // 3、私有化的构造器 并初始化对象属性
    SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    // 4、提供获取枚举类对象的属性的方法
    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
}

使用enum 定义枚举类与 之前的定义方式一样,这4步都要有。但是有一些区别。

首先是一开始就需要定义 公共的静态的不可变属性 接收初始化的对象。这里的修饰符在 Jvm 已经定义好了,我们不需要写上,也不能写上。

然后就是构造器的 私有化修饰符 也不需要写了。默认就是私有化的。可以写,但是不需要写。

其他的声明枚举的属性、获取属性 都需要提供,没什么区别。

1.2.2 enum 类的主要方法

我们使用enum定义的枚举类,默认继承Enum类,会有如下的常用方法

  • values 方法 ,这是一个类方法,返回枚举类型数组。

SeasonEnum[] values = SeasonEnum.values();
for (SeasonEnum value : values) {
    System.out.println(value);
}
// 输出 所有的枚举常量
SPRING
SUMMER
AUTUMN
WINTER
  • valueOf (String str) 方法 返回枚举对象

    SeasonEnum seasonEnum = SeasonEnum.valueOf(SeasonEnum.AUTUMN.toString());
    System.out.println(seasonEnum); // AUTUMN
    ​
    SeasonEnum spring = SeasonEnum.valueOf("SPRING");
    System.out.println(spring); // SPRING
    ​
    SeasonEnum spring = SeasonEnum.valueOf("SP");
    System.out.println(spring); // 报错:java.lang.IllegalArgumentException: No enum constant

    通过传入 枚举类名称字符串,找到对应的枚举对象。如果找不到会报错。

  • toString() 方法 返回当前枚举类对象常量的名称

String s = SeasonEnum.SPRING.toString();
System.out.println(s);

toString是枚举类对象的方法,返回改对象常量的名称

1.2.3 实现接口

1、枚举类也可以像普通类一样,实现接口。然后在枚举类中实现这个接口中的抽象方法。

public interface Info {
​
    /**
     * show 方法
     * @return void
     **/
    void show();
}

有一个Info接口,只有一个show方法。

public enum SeasonEnum implements Info {
    // 1、提供当前枚举类对象,多个对象之间用对象隔开,最后一个使用分号结束。
    SPRING ("春天", "春天春天"),
    SUMMER ("夏天", "夏天夏天"),
    AUTUMN ("秋天", "秋天秋天"),
    WINTER ("冬天", "冬天冬天");
​
    // 2、声明属性  final 定义常量不可变
    private final String seasonName;
    private final String seasonDesc;
​
    // 3、私有化的构造器 并初始化对象属性
    SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    // 4、提供获取枚举类对象的属性的方法
    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
    
    // 实现 show方法
    @Override
    public void show() {
        System.out.println("这是一个季节枚举类");
    }
}
​

上面的代码是直接在枚举类中实现了show方法。这种实现方式,每一个对象都会调用同一个show方法。

枚举类还支持在对象里面实现show方法,达到每一个对象调用自己的show方法。

2、枚举类也可以像普通类一样,在枚举对象中实现接口抽象方法。

public enum SeasonEnum implements Info {
    // 1、提供当前枚举类对象,多个对象之间用对象隔开,最后一个使用分号结束。
    SPRING ("春天", "春天春天"){
        @Override
        public void show() {
            System.out.println("这是一个春天枚举对象");
        }
    },
    SUMMER ("夏天", "夏天夏天") {
        @Override
        public void show() {
            System.out.println("夏天枚举对象");
        }
    },
    AUTUMN ("秋天", "秋天秋天"){
        @Override
        public void show() {
            System.out.println("秋天枚举对象");
        }
    },
    WINTER ("冬天", "冬天冬天"){
        @Override
        public void show() {
            System.out.println("冬天枚举对象");
        }
    };
​
    // 2、声明属性  final 定义常量不可变
    private final String seasonName;
    private final String seasonDesc;
​
    // 3、私有化的构造器 并初始化对象属性
    SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    // 4、提供获取枚举类对象的属性的方法
    public String getSeasonName() {
        return seasonName;
    }
    public String getSeasonDesc() {
        return seasonDesc;
    }
    
}

在 每一个枚举类对象后面加上一对大括号,里面实现抽象方法show 即可。

2、注解

JDK 5.0 开始,java增加了对元数据MetaData 的支持,就是Annotation 注解

注解其实就是代码中的特殊标记。这些标记可以在编译、类加载、运行时被读取。根据标记的不同做相应的处理。

重点:注解本身是没有任何作用的,真正起作用的是 读取注解后的操作。

通过使用注解,就可以在不改变原有代码的基础上,进行功能拓展。

注解可以像修饰符一样被使用,用于修饰 包、类、构造器、方法、成员变量、参数、局部变量声明等等。

这些信息被保存到Annotation的name=value对中。

常见的注解:

@Override

这个注解是加在方法上面,表示这个方法是重写了父类、抽象类、接口等的方法。

编译的时候,当获取到这个注解时,就会对这个方法进行检查,看是否正确的重写的父类或抽象类或接口中的方法。

加上注解:标记这是一个重写方法。

注解真正起作用:在编译时获取这个@Override 注解,执行检查代码。

@Deprecated

用于修饰 类、方法,表示已过时。

加上注解:标记这是一个过时方法,建议不要使用。

起作用:

运行时获取到这个注解,弹出提示这是过时方法,建议用新的替代方法。

@SuppressWarnings

抑制编译器警告

以上三个注解是JDK内置的注解。直接用即可。

2.1 注解的定义

注解的类型是 @interface

@interface 与 interface 没有什么关系。

public @interface MyAnnotation {

    // 定义属性  有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
    String[] value();
}

使用:

@MyAnnotation(value = "你好,注解")
public class TestMain {}

如果只传一个值,建议使用value作为属性名称。这样就可以省略value不写。

@MyAnnotation("你好,注解")
public class TestMain {}

如果需要传多个值,前提是定义的属性是一个数组。可以使用大括号的方式定义。

@MyAnnotation({"你好,注解", "自定义注解"})
public class TestMain {}

定义 带默认值的注解。

public @interface MyAnnotation {

    // 定义属性  有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
    String[] value() default "哈哈哈";
}

有默认值的注解,这个属性就不是必须写了,如果写了,就会替换默认值。

注解并不是一定要有属性,因为注解本身就是一个标记,有名字就可以了,属性根据需要添加。

如果定义了属性,使用时一定要传入。使用时不传入 就一定要在注解定义属性时 声明默认值。

2.2 注解的使用

上一节的使用只是把注解加载某个地方,类或者方法。即在一个地方打上标记。

真正使用时,得利用反射获取到这个注解。然后再干一点事情。

2.3 元注解

用于修饰注解的注解

JDK5.0 提供了4个标准的元注解。

  • Retention 保留: 声明注解的生命周期

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {

    RetentionPolicy value();
}

Retention 注解只有一个 value属性,属性类型是 RetentionPolicy枚举类。枚举对象有

public enum RetentionPolicy {
    /**
     * 源代码时期,编译器将丢弃注解
     */
    SOURCE,

    /**
     class字节码文件时期,运行时会被抛弃。这是默认设置行为。
     */
    CLASS,

    /**
    注释将由编译器记录在类文件中,并且运行时也会保留。可以使用反射获取注解。
     */
    RUNTIME
}
  • Target 目标:声明注解可以修饰的对象,即可以放置的位置

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
   
    ElementType[] value();
}

Target 注解只有一个value属性,是一个ElementType类型的数组。ElementType是一个枚举类。

public enum ElementType {
    /** 类, 接口 (包括注解), 或者枚举 */
    TYPE,

    /** 属性 (包括枚举的常量) */
    FIELD,

    /** 方法 */
    METHOD,

    /** 形式参数 */
    PARAMETER,

    /** 构造器 */
    CONSTRUCTOR,

    /** 局部变量 */
    LOCAL_VARIABLE,

    /** 注解类型 */
    ANNOTATION_TYPE,

    /** 包 */
    PACKAGE,

    /**
     * 参数类型
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     */
    TYPE_USE
}

如何不写 Target 注解修饰,默认是哪里都可以修饰。一般都会写上,指定可以修饰哪些结构。

  • Documented 文档

    用于指定被该注解修饰的注解将被javadoc工具提取成文档。javadoc默认是不包括注解的

    当使用 Documented 注解时,Retention 注解的生命周期必须为 RUNTIME

  • Inherited 继承

被继承注解修饰的注解具有继承性。父类加的注解如果被Inherited 注解修饰,那么子类也会有这个注解。

2.4 JDK8.0 注解新特性

1、支持可重复注解

jdk8之前的写法:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    // 定义属性  有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
    String[] value() default "哈哈哈";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
	// 属性类型是 MyAnnotation 注解数组
    MyAnnotation[] value();
}
// 使用
@MyAnnotations(value = {@MyAnnotation(value = "1"), @MyAnnotation(value = "2")})
public class TestMain {}

JDK8.0 之后的写法:

使用 @Repeatable 注解

@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    // 定义属性  有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
    String[] value() default "哈哈哈";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
	// 属性类型是 MyAnnotation 注解数组
    MyAnnotation[] value();
}
// 使用
@MyAnnotation(value = "1")
@MyAnnotation(value = "2")
public class TestMain {}

这种写法还是需要定义 MyAnnotations 这个注解的,需要在 MyAnnotation注解上加上 Repeatable注解,值为MyAnnotations .class

注意MyAnnotations 与 MyAnnotation 的生命周期(@Retention)和目标位置(@Target)以及 继承性(@Inherited )要一致。

JDK8之前与jdk8之后只是写法不一致,但是意思是一样的。

2、类型注解

就是 @Target 注解值里面 ElementType 枚举类新增的 2个枚举对象 TYPE_PARAMETER、TYPE_USE

public enum ElementType {
	
	。。。。省略其他
    /**
     * 参数类型
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     */
    TYPE_USE
}

1、使用 TYPE_PARAMETER 就可以让注解修饰 参数,泛型等结构。看例子

@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.TYPE_PARAMETER}) // @Target 增加了 ElementType.TYPE_PARAMETER
public @interface MyAnnotation {

    // 定义属性  有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
    String[] value() default "哈哈哈";
}
// 使用:@MyAnnotation T
public class Generic<@MyAnnotation T> {
}

2、使用 TYPE_USE 修饰 泛型参数、强制、异常

@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) // 又增加了ElementType.TYPE_USE
public @interface MyAnnotation {

    String[] value() default "哈哈哈";
}
public class Generic<@MyAnnotation T>  {
	// 1、修饰异常
    public void show() throws @MyAnnotation RuntimeException{
        // 2、修饰强制类型
        int num = (@MyAnnotation int) 10L;
    }
    // 3、修饰泛型参数
    ArrayList<@MyAnnotation String> list = new ArrayList<>();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值