枚举与注解的使用说明

枚举类与注解

  • 枚举类
    • 枚举类的理解
    • 如何定义枚举类?
    • 方式一、自定义枚举类
      • 步骤
      • 案例
    • 方式二、使用 enum 关键字定义枚举类
      • 使用说明
      • Enum 类常用方法
      • Enum 类的主要方法
      • 步骤
      • 案例
    • 实现接口的枚举类
      • 使用 enum 关键字定义的枚举类实现接口的情况
        • 案例
  • 注解
    • 理解
    • 常见的 Annotation
      • 一:生成文档相关的注解
        • 说明
        • 案例
      • 二:在编译时进行格式检查(JDK内置的三个基本注解)
        • 案例
      • 三:跟踪代码依赖性,实现替代配置文件功能
    • 自定义 Annotation(注解)
      • 参照 @SuppressWarnings 注解进行定义
        • @SuppressWarnings 部分源码
        • 步骤
        • 案例
    • JDK 中的元注解
      • 理解
        • JDK5.0 提供了4个标准的 meta-annotation 类型
        • @Retention
        • @Target
        • @Documented 与 @Inherited
    • JDK8 的注解新特性
      • 可重复的注解
        • JDK5 写法
        • JDK8 写法
      • 可用于类型的注解
        • 案例

枚举类

枚举类的理解

  • 类的对象只有有限个,确定的;我们就称此类为枚举类

  • 当需要定义一组常量时,强烈建议使用枚举类

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

如何定义枚举类?

方式一:JDK5.0 之前,自定义枚举类

方式二:JDK5.0 时,可以使用 enum 关键字定义枚举类

方式一、自定义枚举类

步骤

  1. 声明枚举类对象的属性,使用 private final 进行修饰
  2. 私有化类的构造器,并给对象属性进行赋值
  3. 提供当前枚举类所需要的多个对象,使用 public static final 进行修饰
  4. 其它需求:比如重写 toString()、编写 get 方法获取对应属性值等(可选

案例

package com.laoyang.test.day01;

/**
 * @ClassName EnumerateTest
 * @Description: 自定义枚举类
 * @Author Laoyang
 * @Date 2021/9/22 9:22
 */
public class EnumerateTest {
    public static void main(String[] args) {
        Season season = Season.SPRING;
        System.out.println(season.getSeasonName());  // 春季
    }
}

/**
 * 自定义枚举类
 */
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 进行修饰
    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;
    }

    // 提供 toString() 方法
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

方式二、使用 enum 关键字定义枚举类

使用说明

  • 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,因此不能再继承其他类。

  • 枚举类的构造器只能使用 private 权限修饰符。

  • 枚举类的所有实例必须在枚举类中显式列出 (, 分隔 ; 结尾);列出的实例系统会自动添加 public static final 修饰。

  • 必须在枚举类的第一行声明枚举类对象。

JDK 1.5 中可以在 switch 表达式中使用 Enum 定义的枚举类的对象作为表达式,case 子句可以直接使用枚举值的名字,无需添加枚举类作为限定。

Enum 类常用方法

方法 作用
valueOf 可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常: IllegalArgumentException。
values 返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
toString 返回当前枚举类对象常量的名称
equals 比较两个枚举是否相等,主要是为了在Set、List和Map中使用(注意:equals() 是不可变得!!!)
hashCode Enum 实现了 hashCode() 来和 equals() 保持一致,这个方法也是不可变的
getDeclaningClass 得到枚举常量所属枚举类型的 Class 对象;可以用来判断两个枚举常量是否属于同一个枚举类型
name 得到当前枚举常量的名称,建议优先使用 toString()
ordinal 得到当前枚举常量的次序
comparaeTo 枚举类型实现了 Comparable 接口,这样可以比较两个枚举常量的大小(按照声明的顺序排列)
clone 枚举类型不能被 Clone;为了防止类实现克隆方法,Enum 实现了一个仅抛出 CloneNotSupportedException 异常的不变 Clone()

Enum 类的主要方法

  • values() 方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
  • valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的 “名字”。如不是,会有运行时异常: IllegalArgumentException。
  • toString():返回当前枚举类对象常量的名称。

步骤

  1. 提供当前枚举类的对象,多个对象之间用逗号隔开,分号结束。
  2. 声明 Seasons 对象的属性:使用 private final 进行修饰。
  3. 私有化类的构造器,并给对象属性进行赋值(初始化)。
  4. 其它需求:获取枚举类对象的属性(可选)。

案例

package com.laoyang.test.day01;

import org.junit.Test;
import java.util.Arrays;

/**
 * @ClassName EnumTest
 * @Description: JDK5.0 新增的定义枚举类的方式
 * @Author Laoyang
 * @Date 2021/9/23 15:41
 */
public class EnumTest {
    /**
     说明:使用 enum 定义的枚举类默认继承于 java.lang.Enum 类
     */
    @Test
    public void testOne() {
        Seasons autumn = Seasons.AUTUMN;
        System.out.println(autumn);
    }

    /**
     * Enum 类的主要方法
     * values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
     * valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
     * toString():返回当前枚举类对象常量的名称。
     */
    @Test
    public void testTwo() {
        Seasons autumn = Seasons.SPRING;
        // toString() 方法的使用
        System.out.println(autumn.toString());

        System.out.println("---------------------------");

        // values() 方法的使用
        Seasons[] values = Seasons.values();
        System.out.println(Arrays.toString(values));

        System.out.println("---------------------------");

        // 线程中的枚举对象
        Thread.State[] values1 = Thread.State.values();
        System.out.println(Arrays.toString(values1));

        System.out.println("---------------------------");

        // valueOf(String objName) :返回枚举类中对象名为 objName 的对象
        Seasons summer = Seasons.valueOf("SUMMER");
        System.out.println(summer);

        /*
        如果没有 objName 的枚举类对象,则会抛出异常:
         java.lang.IllegalArgumentException: No enum constant com.laoyang.test.day01.Seasons.SUMMERR
         */
        Seasons summerr = Seasons.valueOf("SUMMERR");
        System.out.println(summerr);
    }
}

/**
 * 使用 enum 定义枚举类
 */
enum Seasons {
    // 1. 提供当前枚举类的对象,多个对象之间用逗号隔开,分号结束
    SPRING("春季", "迎春花"),
    SUMMER("夏季", "冰淇淋"),
    AUTUMN("秋季", "枫树林"),
    WINTER("冬季", "堆雪人");

    // 2. 声明 Seasons 对象的属性:使用 private final 进行修饰
    private final String SeasonsName;

    private final String SeasonsDesc;

    // 3. 私有化类的构造器,并给对象属性进行赋值(初始化)
    private Seasons(String SeasonsName, String SeasonsDesc) {
        this.SeasonsName = SeasonsName;
        this.SeasonsDesc = SeasonsDesc;
    }

    // 4. 其它需求:获取枚举类对象的属性
    public String getSeasonsName() {
        return SeasonsName;
    }

    public String getSeasonsDesc() {
        return SeasonsDesc;
    }

    /*
     使用 enum 的方式定义枚举类时,可以不用重写 toString() 方法(有需要时也可以进行重写)
     因为在父类 Enum 类中,将 toString() 进行了重写,最后返回的是变量的名称
     比如调用 SPRING("春季", "迎春花"),最后返回的就是 SPRING
     */
//    @Override
//    public String toString() {
//        return "Seasons{" +
//                "SeasonsName='" + SeasonsName + '\'' +
//                ", SeasonsDesc='" + SeasonsDesc + '\'' +
//                '}';
//    }
}

说明

使用 enum 的方式定义枚举类时,可以不用重写 toString() 方法(有需要时也可以进行重写);因为在父类 Enum 类中,将 toString() 进行了重写,最后返回的是变量的名称。

比如 调用 SPRING(“春季”, “迎春花”),最后返回的就是 SPRING

实现接口的枚举类

  • 和普通 Java 类一样,枚举类可以实现一个或多个接口。
  • 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
  • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式, 则可以让每个枚举值分别来实现该方法。

使用 enum 关键字定义的枚举类实现接口的情况

  • 情况一:实现接口,在 enum 类中实现抽象方法
  • 情况二:让枚举类的对象分别实现接口中的抽象方法
案例
package com.laoyang.test.day01;

/**
 * @ClassName EnumInterface
 * @Description:
 * @Author Laoyang
 * @Date 2021/9/23 16:13
 */
public class EnumInterface {
    public static void main(String[] args) {
        User male = User.MALE;
        male.show();    // 男生

        User female = User.FEMALE;
        female.show();  // 女生
    }
}

interface Info {
    void show();
}

enum User implements Info {
    MALE("小明", 18) {
        @Override
        public void show() {
            System.out.println("男生");
        }
    },
    FEMALE("小美", 17) {
        @Override
        public void show() {
            System.out.println("女生");
        }
    };

    private final String name;
    private final int age;

    private User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /*
     如果是每一个枚举对象都调用同一个方法效果,就只需要在类中重写即可,比如这里打印的 “快乐宝贝!”
     如果是想要每一个枚举对象返回不同的效果,则可以在枚举对象中进行重写,给每一个枚举对象编写对应的需求
     */
//    @Override
//    public void show() {
//        System.out.println("快乐宝贝!");
//    }
}

说明

  • 如果是每一个枚举对象都调用同一个方法效果,就只需要在类中重写即可,比如这里打印的 “快乐宝贝!”
  • 如果是想要每一个枚举对象返回不同的效果,则可以在枚举对象中进行重写,给每一个枚举对象编写对应的需求

注解

理解

  • 从 JDK 5.0 开始,Java 增加了对元数据(MetaData) 的支持,也就是 Annotation(注解)。

  • Annotation 其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用 Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。

  • Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、 成员变量、参数、局部变量的声明,这些信息被保存在 Annotation 的 “name=value” 中。

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

常见的 Annotation

使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。

一:生成文档相关的注解

@author 标明开发该类模块的作者,多个作者之间使用,分割

@version 标明该类模块的版本

@see 参考转向,也就是相关主题

@since 从哪个版本开始增加的

@param 对方法中某参数的说明,如果没有参数就不能写

@return 对方法返回值的说明,如果方法的返回值类型是 void 就不能写

@exception 对方法可能抛出的异常进行说明 ,如果方法没有用 throws 显式抛出的异常就不能写其中

说明

@param、@return 和 @exception 这三个标记都是只用于方法的。

@param的格式要求:@param 形参名 形参类型形参说明。

@return 的格式要求:@return 返回值类型返回值说明。

@exception 的格式要求:@exception 异常类型异常说明。

@param 和 @exception 可以并列多个。

顺带一提,IDEA 中可以设置类、接口等在创建的时候自动生成头文件,例如:

/**
 * @ClassName ${NAME}
 * @Description: ${Description}
 * @Author ${USER}
 * @Date ${DATE} ${TIME}
*/

设置位置:File - Settings - Editor - File and Code Templates - File Header

案例
package com.laoyang.test.day02;

/**
* @author Laoyang
* @version 1.0
* @see Math.java
*/
public class Annotation {
    /**
     * 主方法
     * @param args
     */
    public static void main(String[] args) {}
    
    /**
     * 求圆面积的方法
     * @param radius 半径值
     * @return 圆的面积
     */
    public double getArea(double radius){
    	return Math.PI * radius * radius;
    }
}

二:在编译时进行格式检查(JDK内置的三个基本注解)

  • @Override:限定重写父类方法,该注解只能用于方法。
  • @Deprecated:用于表示所修饰的元素 (类,方法等) 已过时,通常是因为所修饰的结构危险或存在更好的选择。
  • @SuppressWarnings:抑制编译器警告。
案例
package com.laoyang.test.day02;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;

/**
 * @ClassName AnnotationTest
 * @Description: 注解的使用
 * @Author Laoyang
 * @Date 2021/9/23 16:53
 */
public class AnnotationTest {
    /**
     二:在编译时进行格式检查(JDK内置的三个基本注解)
     * @Override: 限定重写父类方法, 该注解只能用于方法
     * @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
     * @SuppressWarnings: 抑制编译器警告
     */
    @Test
    public void testOne() {
        Student student = new Student();
        student.print();
        
        // 像这种带一个删除线(在编译器中可见)的,就表示是过时了的(使用了 @Deprecated 进行标识)
        Date date = new Date(2021, 10, 1);

        /*
         大家可以观察下面两个 list 的颜色
         加了注解的颜色就跟在使用时的颜色一样(比较亮)
         没加注解的颜色就是平时创建好一个变量但是又没有使用的时候一样(比较暗淡)
         */
        @SuppressWarnings("unused")
        ArrayList list = new ArrayList();

        ArrayList list1 = new ArrayList();
    }
}

interface Info {
    void show();
}

class Student implements Info {
    /**
     * 在重写父类方法的时候就会加上 @Override 注解
     * 加上之后如果方法名对不上父类的,那么就会提示有错误(也算是一种标识,可以让人一眼就发现这个方法是重写的)
     */
    @Override
    public void show() {
        System.out.println("嘿嘿嘿");
    }
    
	@Deprecated
    public void print() {
        System.out.println("过时的方法")
    }
}

如果在这里看的话,肯定是看不出什么效果的,所以还是要放入编译器中进行查看,效果才会比较明显

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

emmmm…这个就比如我们使用的 controller 层用的路径那些注解,旧一点的 JSP 中也会用到

因为这里只是给大家介绍枚举类和注解,让大家有一个了解与认识,所以就不多说了

自定义 Annotation(注解)

  • 定义新的 Annotation 类型使用 @interface 关键字

  • 自定义注解自动继承了 java.lang.annotation.Annotation 接口

  • Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数;类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、 以上所有类型的数组

    如果只是想要一个参数的话也可以直接定义普通的成员变量,而不定义成数组类型

  • 可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字

  • 如果只有一个参数成员,建议使用参数名为 value

  • 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是 “参数名 = 参数值” ,如果只有一个参数成员,且名称为 value, 可以省略 “value=”

  • 没有成员定义的 Annotation 称为标记,包含成员变量的 Annotation 称为元数据 Annotation

注意:自定义注解必须配上注解的信息处理流程才有意义

参照 @SuppressWarnings 注解进行定义

@SuppressWarnings 部分源码
/*
 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

/**
 * Indicates that the named compiler warnings should be suppressed in the
 * annotated element (and in all program elements contained in the annotated
 * element).  Note that the set of warnings suppressed in a given element is
 * a superset of the warnings suppressed in all containing elements.  For
 * example, if you annotate a class to suppress one warning and annotate a
 * method to suppress another, both warnings will be suppressed in the method.
 *
 * 

As a matter of style, programmers should always use this annotation * on the most deeply nested element where it is effective. If you want to * suppress a warning in a particular method, you should annotate that * method rather than its class. * * @author Josh Bloch * @since 1.5 * @jls 4.8 Raw Types * @jls 4.12.2 Variables of Reference Type * @jls 5.1.9 Unchecked Conversion * @jls 5.5.2 Checked Casts and Unchecked Casts * @jls 9.6.3.5 @SuppressWarnings */ @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @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 not 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. * *

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(); }

步骤
  1. 使用 @interface 关键字进行定义
  2. 内部定义成员变量,通常使用 value 表示
  3. 可以指定成员的默认值,使用 default 定义
  4. 如果自定义注解没有成员变量,表明是一个标识作用

注意:

  • 如果该注解有成员变量,在使用注解的时候就需要指明成员变量的值(也可以在注解中设置默认值)
  • 自定义注解必须配上注解的信息处理流程(使用反射)才有意义
  • 自定义注解通常都会指明两个元注解:@Retention 和 @Target
案例

自定义注解

package com.laoyang.test.day02;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotation
 * @Description: 自定义注解
 * @Author Laoyang
 * @Date 2021/9/23 17:28
 */
// 添加 Target 注解可以限制当前自定义注解的使用范围(比如类、方法、接口等)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
// 修饰注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 定义多个参数值的时候就可以将该变量设置为一个数组,如果只需要一个,那么就直接定义一个变量即可
    // String value();

    // 指定默认值,指定默认值之后再使用该注解的时候就可以不带参数了(如果不指定默认值,那么在使用的时候就需要手动赋值)
    String value() default "hello";
}

测试类

package com.laoyang.test.day02;

import org.junit.Test;
import java.lang.annotation.Annotation;

/**
 * @ClassName AnnotationTest
 * @Description: 自定义注解的使用
 * @Author Laoyang
 * @Date 2021/9/23 16:53
 */
public class AnnotationTest {
    public static void main(String[] args) {
    }
}

// 带参(不管有没有默认值,都是可以带参的,只不过没有默认值的情况下必须带参,而有默认值的时候则可选择性带参)
@MyAnnotation("hi")
class Person {
    private String name;
    private int age;

    public Person() {
    }

    // 不带参,使用默认值
    @MyAnnotation
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

JDK 中的元注解

理解

元注解:对现有的注解进行解释说明的注解

元数据:对现有数据的修饰的数据就被称为元数据

JDK5.0 提供了4个标准的 meta-annotation 类型
  • @Retention: 指定所修饰的 Annotation 的生命周期:SOURCE / CLASS(默认行为)/ RUNTIME

    只有被声明为 RUNTIME 生命周期的注解才能够通过反射获取。

  • @Target: 用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素。

  • @Documented: 表示所修饰的注解在被 javadoc 解析时,保留下来。

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

在使用过程中可查看对应注解的底层代码,然后进行理解

@Retention

只能用于修饰一个 Annotation 定义,用于指定该 Annotation 的生命周期,@Rentention 包含一个 RetentionPolicy 类型的成员变量,使用 @Rentention 时必须为该 value 成员变量指定值:

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释
  • RetentionPolicy.CLASS:在 class 文件中有效(即 class 保留),当运行 Java 程序时,JVM 不会保留注解(这是默认值)
  • RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java 程序时,JVM 会保留注释(程序可以通过反射获取该注释)
@Target
  • 用于修饰 Annotation 定义,用于指定被修饰的 Annotation 能用于修饰哪些程序元素;

  • @Target 也包含一个名为 value 的成员变量。

参数 作用 参数 作用
TYPE 用于描述类、接口或enum声明 LOCAL_VARIABLE 用于描述局部方法
FIELD 用于描述域 ANNOTATION_TYPE 注解类型声明
METHOD 用于描述方法 PACKAGE 用于描述包
PARAMETER 用于描述参数 TYPE_PARAMETER JDK1.8新增,类型参数声明
CONSTRUCTOR 用于描述构造器 TYPE_USE JDK1.8新增,类型的使用

以上就是 @Target 中的各个参数,大家可根据需要进行使用

@Documented 与 @Inherited

因为在实际使用中,这两个注解的使用率并不是很高,所以这里就不多说了,大家先暂时理解一下上面的介绍即可

JDK8 的注解新特性

  • 可重复的注解

  • 可用于类型的注解

可重复的注解

说明:顾名思义,就是在同一个地方使用多次相同注解进行修饰;

JDK5 写法

自定义注解A

package com.laoyang.test.day02;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotation
 * @Description: 自定义注解
 * @Author Laoyang
 * @Date 2021/9/23 17:28
 */
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

自定义注解B

package com.laoyang.test.day02;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotations
 * @Description: JDK 8 注解新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:19
 */
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    // JDK8 之前使用重复注解的方式,定义自定义注解A的成员变量
    MyAnnotation[] value();
}

测试类

package com.laoyang.test.day02;

/**
 * @ClassName AnnotationTests
 * @Description: JDK8 注解的新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:22
 */
public class AnnotationTests {
    public static void main(String[] args) {
        User user = new User();
        user.setName("小迷糊");
        System.out.println(user.getName());
    }
}

// JDK8 之前使用重复注解的方式
@MyAnnotations({@MyAnnotation("hi"),@MyAnnotation("ha")})
class User {
    private String name;
    private int age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

因为之前都介绍过这些注解和变量的作用了,所以这里就简单点给大家展示了

JDK8 写法
  • 自定义注解A 上声明 @Repeatable 注解,成员值为 自定义注解B.class
  • 自定义注解A 的 Target 和 Retention 等元注解需要与 自定义注解B 相同

自定义注解A

package com.laoyang.test.day02;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotation
 * @Description: 自定义注解
 * @Author Laoyang
 * @Date 2021/9/23 17:28
 */
// @Repeatable:JDK8 新特性,重复注解
@Repeatable(MyAnnotations.class)
// 添加Inherited 注解可以让使用当前自定义注解修饰的类的子类拥有相同的效果
@Inherited
// 添加 Target 注解可以限制当前自定义注解的使用范围
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
// 修饰注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 指定默认值,指定默认值之后再使用该注解的时候就可以不带参数了
    String value() default "hello";
}

自定义注解A 中的注解需要和 自定义注解B 中保持一致,但是需要加上关键注解 @Repeatable(只有这个注解和自定义注解B不一样)

自定义注解B

package com.laoyang.test.day02;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotations
 * @Description: JDK 8 注解新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:19
 */
// 添加Inherited 注解可以让使用当前自定义注解修饰的类的子类拥有相同的效果
@Inherited
// 添加 Target 注解可以限制当前自定义注解的使用范围
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
// 修饰注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    // JDK8 之前使用重复注解的方式
    MyAnnotation[] value();
}

自定义注解B 中不需要加入 @Repeatable 注解

测试类

package com.laoyang.test.day02;

/**
 * @ClassName AnnotationTests
 * @Description: JDK8 注解的新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:22
 */
public class AnnotationTests {
    public static void main(String[] args) {
        User user = new User();
        user.setName("小迷糊");
        System.out.println(user.getName());
    }
}

// JDK8 之前使用重复注解的方式
//@MyAnnotations({@MyAnnotation("hi"),@MyAnnotation("ha")})
// JDK8 之后使用重复注解的方式
@MyAnnotation("hi")
@MyAnnotation("ha")
class User {
    private String name;
    private int age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

可用于类型的注解

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

自定义注解A

package com.laoyang.test.day02;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotation
 * @Description: 自定义注解
 * @Author Laoyang
 * @Date 2021/9/23 17:28
 */
// @Repeatable:JDK8 新特性,重复注解
@Repeatable(MyAnnotations.class)
// 添加Inherited 注解可以让使用当前自定义注解修饰的类的子类拥有相同的效果
@Inherited
/*
 添加 Target 注解可以限制当前自定义注解的使用范围
 JDK8 新参数:TYPE_PARAMETER(修饰泛型) 和 TYPE_USE
 */
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER, TYPE_USE})
// 修饰注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 指定默认值,指定默认值之后再使用该注解的时候就可以不带参数了
    String value() default "hello";
}

主要的还是 @Target 注解中的参数,TYPE_PARAMETER 与 TYPE_USE

自定义注解B

package com.laoyang.test.day02;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

/**
 * @ClassName MyAnnotations
 * @Description: JDK 8 注解新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:19
 */
// 添加Inherited 注解可以让使用当前自定义注解修饰的类的子类拥有相同的效果
@Inherited
// 添加 Target 注解可以限制当前自定义注解的使用范围
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
// 修饰注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    // JDK8 之前使用重复注解的方式
    MyAnnotation[] value();
}

测试类

package com.laoyang.test.day02;

import java.util.ArrayList;

/**
 * @ClassName AnnotationTests
 * @Description: JDK8 注解的新特性
 * @Author Laoyang
 * @Date 2021/9/24 15:22
 */
public class AnnotationTests { 

}

// JDK8 新特性:类型注解(主要就是两个参数)
// TYPE_PARAMETER
class Generic<@MyAnnotation T> {
    // TYPE_USE
    public void show() throws @MyAnnotation RuntimeException {
        ArrayList<@MyAnnotation String> list = new ArrayList<>();

        int i = (@MyAnnotation int) 20L;
    }
}

你可能感兴趣的:(Java,java,枚举类)