2021-11-01 十二、枚举与注解

1. 枚举类的使用

1.1 基本介绍

假如类的对象个数有限并且是确定的,那么它可以使用枚举类。
例如:

  1. 星期:Monday(星期一)、…、Sunday(星期天)
  2. 性别:Man(男)、Woman(女)
  3. 季节:Spring(春节)…Winter(冬天)
  4. 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)
  5. 就职状态:Busy、Free、Vocation、Dimission
  6. 订单状态:Nonpayment(未付款)、Paid(已付款)、Delivered(已发货)、Return(退货)、Checked(已确认)Fulfilled(已配货)、
  7. 线程状态:创建、就绪、运行、阻塞、死亡

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

1.2 枚举类的实现

JDK1.5之前需要自定义枚举类
JDK 1.5 新增的enum 关键字用于定义枚举类
若枚举只有一个对象, 则可以作为一种单例模式的实现方式。
(单例模式的第三种实现方式-枚举法实现,如果忘记了懒汉式和饿汉式的实现可以翻回前面看看)

1.2.1 自定义枚举类

自定义枚举类的时候注意它的属性有以下特点:

  1. 枚举类对象的属性不可以被改动, 所以应该使用private final修饰
  2. 属性应该在构造器中为其赋值,并且构造器也应该为私有的,防止外界调用
//自定义枚举类
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("春天","万物复苏");
    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;
    }

    //4.其他诉求1:提供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='春天', seasonDesc='万物复苏'}
        System.out.println(spring.getSeasonName()); // 春天
        System.out.println(spring.getSeasonDesc()); // 万物复苏
    }
}

1.2.2 使用enum关键字定义枚举类

使用说明:

  1. 使用enum定义的枚举类默认继承了java.lang.Enum类,因此不能再继承其他类
  2. 枚举类的构造器只能使用private 权限修饰符
  3. 枚举类的所有实例必须在枚举类中显式列出(使用,进行分隔,使用;来作为结尾)。列出的实例系统会自动添加public static final 修饰
  4. 必须在枚举类的第一行声明枚举类对象

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

//使用enum关键字枚举类
enum Season1{
    //1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
    SPRING("春天","万物复苏"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","金秋送爽"),
    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.其他诉求:获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //4.其他诉求1:提供toString()
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}
public class SeasonTest1 {
    public static void main(String[] args) {        
    	Season1 summer = Season1.SUMMER;
        // 如果没重写toString会输出SUMMER,重写了之后会输出里面的内容
        System.out.println(Season1.SUMMER); // SUMMER
        System.out.println(summer.toString()); // Season{seasonName='夏天', seasonDesc='烈日炎炎'}

        System.out.println(Season1.class.getSuperclass()); // class java.lang.Enum
    }
}

枚举类的常用方法
在这里插入图片描述
主要使用的方法有:

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

//        System.out.println(Season1.class.getSuperclass());
        System.out.println("**************************");
        //values():返回所有的枚举类对象构成的数组
        Season1[] values = Season1.values();
        for(int i = 0;i < values.length;i++){
            System.out.println(values[i]);
        }
        /*
        SPRING
		SUMMER
		AUTUMN
		WINTER 
        */
        System.out.println("****************************");
        Thread.State[] values1 = Thread.State.values();
        for(int i = 0;i < values1.length;i++){
        	// 同样会打印出Thread的State枚举类中的对象名
            System.out.println(values1[i]);
        }

        //valueOf(String objName):返回枚举类中对象名是objName的对象。
        Season1 winter = Season1.valueOf("WINTER");
        //如果没有objName的枚举类对象,则抛异常:IllegalArgumentException
//        Season1 winter = Season1.valueOf("WINTER1");
        System.out.println(winter);

    }
}

//使用enum关键字枚举类
enum Season1{
    //1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
    SPRING("春天","万物复苏"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","金秋送爽"),
    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.其他诉求:获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //4.其他诉求1:提供toString()
//    @Override
//    public String toString() {
//        return "Season{" +
//                "seasonName='" + seasonName + '\'' +
//                ", seasonDesc='" + seasonDesc + '\'' +
//                '}';
//    }
}

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

使用enum关键字定义的枚举类实现接口的情况
情况一:实现接口,在enum类中实现抽象方法
情况二:让枚举类的对象分别实现接口中的抽象方法

interface Info{
    void show();
}

//使用enum关键字枚举类
enum Season1 implements Info{
    //情况二:让枚举类的对象分别实现接口中的抽象方法
    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.声明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.其他诉求:获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //4.其他诉求1:提供toString()
//    @Override
//    public String toString() {
//        return "Season{" +
//                "seasonName='" + seasonName + '\'' +
//                ", seasonDesc='" + seasonDesc + '\'' +
//                '}';
//    }
//    情况一:实现接口,在enum类中实现抽象方法
//    @Override
//    public void show() {
//        System.out.println("这是一个季节。");
//    }
}
public class SeasonTest1 {
    public static void main(String[] args) {
        // 情况一:实现接口,在enum类中实现抽象方法,记得注释掉对象中重写的抽象方法,不然会调用对象中的方法
        Season1.WINTER.show();
        // 情况二:让枚举类的对象分别实现接口中的抽象方法
        //values():返回所有的枚举类对象构成的数组
        Season1[] values = Season1.values();
        for(int i = 0;i < values.length;i++){
            System.out.println(values[i]);
            values[i].show();
        }

        //valueOf(String objName):返回枚举类中对象名是objName的对象。
        Season1 winter = Season1.valueOf("WINTER");
        winter.show();
    }
}

2. 注解的使用

2.1 基本介绍

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

什么是Annotation(注解):

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

2.2 Annotation的使用示例

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

示例1:生成文档相关的注解

  1. @author标明开发该类模块的作者,多个作者之间使用,分割
  2. @version标明该类模块的版本
  3. @see参考转向,也就是相关主题
  4. @since从哪个版本开始增加的
  5. @param对方法中某参数的说明,如果没有参数就不能写
  6. @return对方法返回值的说明,如果方法的返回值类型是void就不能写
  7. @exception对方法可能抛出的异常进行说明,如果方法没有用throws显式抛出的异常就不能写其中

注意:@param/@return/@exception这三个标记都是只用于方法的。
@param的格式要求:@param 形参名 形参类型 形参说明
@return的格式要求:@return 返回值类型 返回值说明
@exception的格式要求:@exception 异常类型 异常说明
@param和@exception可以并列多个

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

  1. @Override: 限定重写父类方法, 该注解只能用于方法
  2. @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
  3. @SuppressWarnings: 抑制编译器警告,其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。

@SuppressWarnings的参数如下

参数作用
allto suppress all warnings (抑制所有警告)
boxingto suppress warnings relative to boxing/unboxing operations(抑制装箱、拆箱操作时候的警告)
castto suppress warnings relative to cast operations (抑制映射相关的警告)
dep-annto suppress warnings relative to deprecated annotation(抑制启用注释的警告)
deprecationto suppress warnings relative to deprecation(抑制过期方法警告)
fallthroughto suppress warnings relative to missing breaks in switch statements(抑制确在switch中缺失breaks的警告)
finallyto suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告)
hidingto suppress warnings relative to locals that hide variable(抑制与隐藏变量的局部变量相关的警告)
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句)
nlsto suppress warnings relative to non-nls string literals(忽略非nls格式的字符)
nullto suppress warnings relative to null analysis(忽略对null的操作)
rawtypesto suppress warnings relative to un-specific types when using generics on class params(使用generics时忽略没有指定相应的类型)
restrictionto suppress warnings relative to usage of discouraged or forbidden references(禁止使用与劝阻或禁止使用的引用有关的警告)
serialto suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在serializable类中没有声明serialVersionUID变量)
static-accessto suppress warnings relative to incorrect static access(抑制不正确的静态访问方式警告)
synthetic-accessto suppress warnings relative to unoptimized access from inner classes(抑制子类没有按最优方法访问内部类的警告)
uncheckedto suppress warnings relative to unchecked operations(抑制没有进行类型检查操作的警告)
unqualified-field-accessto suppress warnings relative to field access unqualified (抑制没有权限访问的域的警告)
unusedto suppress warnings relative to unused code (抑制没被使用过的代码的警告)

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

  1. Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署,比如@WebServlet("/login")
    在这里插入图片描述

  2. spring框架中关于事务的管理
    在这里插入图片描述

这俩后面再说

import java.util.ArrayList;
import java.util.Date;

class Person{
    private String name;
    private int age;

    public Person() {
        super();
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void walk(){
        System.out.println("学习中……");
    }
    public void eat(){
        System.out.println("摸鱼中……");
    }
}

interface Info{
    void show();
}

class Student extends Person implements Info{

    @Override
    public void walk() {
        System.out.println("喷子走开");
    }

    @Override
    public void show() {

    }
}
public class AnnotationTest {
    public static void main(String[] args) {
        Person p = new Student();
        p.walk();

        Date date = new Date(2020, 10, 11);
        System.out.println(date);

        @SuppressWarnings("unused")
        int num = 10;

//        System.out.println(num);

        @SuppressWarnings({ "unused", "rawtypes" })
        ArrayList list = new ArrayList();
    }
}

2.3 如何自定义注解

定义新的Annotation类型使用@interface关键字
注意:

  1. 自定义注解自动继承了java.lang.annotation.Annotation接口
  2. Annotation的成员变量在Annotation定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数。类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组
  3. 在定义Annotation的成员变量时为其指定初始值可使用default关键字
  4. 如果只有一个参数成员,建议使用参数名:value
  5. 如果定义的注解含有配置参数那么使用时必须指定参数值,除非它有默认值,格式是参数名=参数值。如果只有一个参数成员,且名称为value,可以省略value=
  6. 没有成员定义的Annotation称为标记(例如@Documented);包含成员变量的Annotation称为元数据(Metadata
  7. 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
  8. 自定义注解通常都会指明两个元注解:@Retention(用于描述注解的生命周期)、@Target(表示注解作用范围)
// 参照@SuppressWarnings定义

// ① 注解声明为:@interface
public @interface MyAnnotation {
	// ② 内部定义成员,通常使用value表示
    String value();
    // ③ 可以指定成员的默认值,使用default定义
    // String value() default "MyAnnotation";
}

@MyAnnotation("hello")
public class JavaDemo40 {
    @MyAnnotation
    public static void main(String[] args) {
	}
}

2.4 jdk中4个基本的元注解的使用

JDK 的元Annotation是为了用于修饰其他Annotation的定义而产生
一共四个:
Retention:指定所修饰的Annotation的生命周期
Target
Documented
Inherited

这里顺便介绍一下什么是元数据:例如String name = “MyBlog”;在这时候,String name就是元数据(对现有数据"MyBlog"修饰的数据就叫元数据)

那么可以理解元注解就是对现有注解修饰的注解

2.4.1 @Retention

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

RetentionPolicy详解

  1. RetentionPolicy.SOURCE:在源文件中有效(即源文件保留,也称源码注解),编译器直接丢弃这种策略的注释(也就是在javac的时候就抛弃了)。它主要用于编译时的校验,校验结束就丢弃了
  2. RetentionPolicy.CLASS:在class文件中有效(即class保留,也称编译时注解),当运行Java 程序时, JVM 不会保留注解。这是默认值(需要配合注解处理器来进行使用,主要用于编译时的预处理)
  3. RetentionPolicy.RUNTIME:在运行时有效(即运行时保留,也称运行时注解),当运行Java 程序时, JVM 会保留注释,加载到内存中,因此程序可以通过反射获取该注释。
    在这里插入图片描述
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 编译之后看Person.class文件可以发现里面并没有@MyAnnotation的相关注解
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {

    String value();

}

public class AnnotationTest {
    public static void main(String[] args) {

    }
}

@MyAnnotation(value = "hello")
class Person{
    private String name;
    private int age;

    public Person() {
        super();
    }

    @MyAnnotation(value = "jack")
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void walk(){
        System.out.println("学习中……");
    }
    public void eat(){
        System.out.println("摸鱼中……");
    }
}

2.4.2 @Target

用于修饰Annotation 定义, 用于指定被修饰的Annotation 能用于修饰哪些程序(如类、方法等)元素。@Target 也包含一个名为value 的成员变量。

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

value的取值
在这里插入图片描述
如上面@Target、@Retention的源码中,所用的ANNOTATION_TYPE就是表示@Target注解用于修饰Annotation
jdk1.8中新增的那俩放下面说

2.4.3 @Documented

用于指定被该元Annotation 修饰的Annotation 类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。
定义为Documented的注解必须设置Retention值为RUNTIME

2.4.4 @Inherited

被它修饰的Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的Annotation, 则其子类将自动具有该注解。
比如:如果把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以继承父类类级别的注解
注意:

  1. 接口用上个@Inherited修饰的注解,其实现类不会继承这个注解
  2. 父类的方法用了@Inherited修饰的注解,子类也不会继承这个注解
  3. 当用了@Inherited修饰的注解的@Retention是RetentionPolicy.RUNTIME,则增强了继承性,在反射中可以获取得到
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 子类则可以继承父类类级别的注解
@interface ATable {
    String name() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface BTable {
    String name() default "";
}
@ATable(name = "aaa")
class Super {
    private int superx;
    public int supery;

    public Super() {
    }
    //私有
    private int superX(){
        return 0;
    }
    //公有
    public int superY(){
        return 0;
    }

}
@BTable(name = "bbb")
class Sub extends Super {
    private int subx;
    public int suby;

    private Sub() {
    }

    public Sub(int i) {
    }

    //私有
    private int subX() {
        return 0;
    }
    //公有
    public int subY() {
        return 0;
    }

}
public class JavaDemo41 {
    public static void main(String[] args) {

        Class<Sub> clazz = Sub.class;
        System.out.println("============================AnnotatedElement===========================");
        System.out.println(Arrays.toString(clazz.getAnnotations()));    //获取自身和父亲的注解。如果@ATable未加@Inherited修饰,则获取的只是自身的注解而无法获取父亲的注解。
        System.out.println("------------------");
    }
}

2.5 利用反射获取注解信息

JDK 5.0java.lang.reflect包下新增了AnnotatedElement接口, 该接口代表程序中可以接受注解的程序元素
当一个Annotation 类型被定义为运行时Annotation后, 该注解才是运行时可见, 当class文件被载入时保存在class 文件中的Annotation 才会被虚拟机读取

程序可以调用AnnotatedElement对象的如下方法来访问Annotation 信息
在这里插入图片描述

public class JavaDemo41 {
    public static void main(String[] args) {
        // 获取sub类
        Class<Sub> clazz = Sub.class;
        System.out.println("============================ getAnnotations ===========================");
        // 获取自身和父亲的注解。如果@ATable未加@Inherited修饰,则获取的只是自身的注解而无法获取父亲的注解。
        System.out.println(Arrays.toString(clazz.getAnnotations()));

        System.out.println("=============== getAnnotation ================");
        // 获取指定注解的属性
        final ATable annotation = clazz.getAnnotation(ATable.class);
        System.out.println(annotation.name());
        System.out.println("--------- getDeclaredAnnotations ---------");
        // 获取方法返回该元素上直接存在的所有注释,此方法忽略了继承的注释
        final Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
        System.out.println(Arrays.toString(declaredAnnotations));
        for (Annotation declaredAnnotation : declaredAnnotations) {
            if (declaredAnnotation instanceof BTable) {
                System.out.println(((BTable) declaredAnnotation).name());
            }
        }
        // 获取sub类的构造器
        final Constructor<?>[] constructors = Sub.class.getConstructors();
        // 获取方法返回该元素上直接存在的所有注释,此方法忽略了继承的注释
        for (Constructor<?> constructor : constructors) {
            final Annotation[] constructorDeclaredAnnotations = constructor.getDeclaredAnnotations();
            for (Annotation constructorDeclaredAnnotation : constructorDeclaredAnnotations) {
                if (constructorDeclaredAnnotation instanceof MyAnnotation) {
                    System.out.println(((MyAnnotation) constructorDeclaredAnnotation).value());
                }
            }
        }

        System.out.println("--------- isAnnotationPresent ---------");
        // 检查此类中是否存在指定注释类型的注释
        System.out.println(clazz.isAnnotationPresent(ATable.class));
        // 检查此构造器中是否存在指定注释类型的注释
        System.out.println(constructors[0].isAnnotationPresent(MyAnnotation.class));
    }
}

2.6 JDK8新特性

JDK8对注解处理提供了两点改进:可重复的注解可用于类型的注解。此外,反射也得到了加强,在Java8中能够得到方法参数的名称。这会简化标注在方法参数上的注解。

2.6.1 可重复注解@Repeatable

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {

    MyAnnotation[] value();
}

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

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
public @interface MyAnnotation {

    String value() default "hello";

}
import java.lang.annotation.Annotation;

@MyAnnotation(value = "hi")
@MyAnnotation(value = "abc")
//jdk 8之前的写法:繁琐
//@MyAnnotations({@MyAnnotation(value="hi"),@MyAnnotation(value="hi")})
public class AnnotationTest {
    public static void main(String[] args) {
    }
}

注意,使用的时候MyAnnotation的Target、Retention、Inherited等元注解要与MyAnnotations相同

2.6.2 类型注解

JDK1.8之后,关于元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPE_USE
在JDK1.8之前,注解只能是在声明的地方所使用,Java8开始,注解可以应用在任何地方
1.ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中(如:泛型声明)。
2.ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中。

import java.lang.annotation.*;

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

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
public @interface MyAnnotation {

    String value() default "hello";

}

import java.util.ArrayList;

public class AnnotationTest {
 
}


class Generic<@MyAnnotation T>{

    public void show() throws @MyAnnotation RuntimeException{

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

        int num = (@MyAnnotation int) 10L;
    }

}

以方法中的注解获取为例,因为比较常用
拿到之后就可以做一系列的操作了(例如以后开发可能会接触到的@Validated)

public class JavaDemo41 {

    public static void test(@MyAnnotation String name) {
        System.out.println("aaa");
    }
    public static void main(String[] args) {
        System.out.println("------------ jdk8新特性,获取方法中形参的注解 --------------- ");

        final Class<JavaDemo41> javaDemo41Class = JavaDemo41.class;
        final Method[] methods = javaDemo41Class.getMethods();
        final Method method = methods[0];
        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (Annotation[] parameterAnnotation : parameterAnnotations) {
            for (Annotation annotation1 : parameterAnnotation) {
                System.out.println(annotation1); // @javabase.three.MyAnnotation(value=MyAnnotation)
            }
        }
    }
}
上一篇总目录下一篇
十一、常用类Java基础总目录十三、集合
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值