注解简介

  • 所有的注解都继承自**java.lang.annotation.Annotation**
  • 注解就是个特殊的类。注解能实现各种功能本质是定义的功能实现类去检查一个类是否运用某个注解,如果存在则通过反射去构建注解类对象,通过对象就能拿到用户的各种注解参数

    • 所以个人猜测是使用一个注解就相当于动态生成了一个注解类对象,并动态插入。如A类使用了B注解,相当于A类持有了一个B类对象

      注解分类

  • 第一类是由编译器使用的注解(如Override,并不影响运行效果,类似于提升

    • 这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。
  • 第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。
    • 这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
  • 第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解

    • 例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。

      注解参数

  • 定义一个注解时,还可以定义配置参数。参数必须是常量,所以参数类型为以下类型:

    • 所有基本类型;
    • String;
    • 枚举类型;
    • 基本类型、String、Class以及枚举的数组。
  • 注解的配置参数可以有默认值,缺少某个配置参数时将使用默认值。

    • 此外,大部分注解会有一个名为value的配置参数,对此参数赋值,可以只写常量,相当于省略了value参数。
    • 如果只写注解,相当于全部使用默认值。

      1. @Check(min=0, max=100, value=55)
      2. public int n;
      3. @Check(value=99) //这第二个与第三个等同
      4. public int p;
      5. @Check(99) // =@Check(value=99)
      6. public int x;
      7. @Check //全部使用默认
      8. public int y;

      定义注解

      注解定义步骤如下

  1. 使用@interface语法来定义注解
  2. 定义参数:注解参数类似于无参方法,参数可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value
    1. 注解类对象获取字段只能通过参数方法获取,不能直接对象调用成员属性
  3. 使用元注解进行注解配置
    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface Report {
    4. int type() default 0;
    5. String level() default "info";
    6. String value() default "";
    7. }

    元注解

  • 有一些注解可以修饰其他注解,这些注解就称为元注解。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。
  • 下面的几个元注解都是使用在类上。点进元注解的定义可以看到都使用了type类型的**@Target**

    @Target 使用位置

  • 用于标注注解能被使用在哪些位置,使用时直接传入如下参数。多个参数时使用数组形式@Target({?,?})

    • 类或接口:ElementType.TYPE
    • 字段:ElementType.FIELD
    • 方法:ElementType.METHOD
    • 构造方法:ElementType.CONSTRUCTOR
    • 方法参数:ElementType.PARAMETER
      1. @Target(ElementType.METHOD) //表示report注解只能用于方法上
      2. public @interface Report { int type() default 0; }

      @Retention 生命周期

  • 定义注解的生命周期。有如下参数:

    • 仅编译期:RetentionPolicy.SOURCE
    • 仅class文件:RetentionPolicy.CLASS@Retention未设置时,class为默认的生命周期
    • 运行期:RetentionPolicy.RUNTIME
  • 我们定义的注解一般都是运行时注解!所以务必指定@Retention

    1. @Retention(RetentionPolicy.RUNTIME)
    2. public @interface Report {}

    @Repeatable 是否可重复使用

  • 表明是否可以重复使用,不怎么使用。参数为注解的class ```java @Repeatable(Reports.class) @Target(ElementType.TYPE) public @interface Report { }

使用时我们可以使用多个report注解 @Report) @Report() public class Hello { }

  1. <a name="Yi3kU"></a>
  2. ### @Inherited 是否允许被继承
  3. - **使用@Inherited定义子类是否可继承父类定义的Annotation。@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效:**
  4. - 在使用的时候,如果一个类用到了@Report,那么他的子类默认也使用了该注解
  5. <a name="jWp4j"></a>
  6. # 处理(读取)注解
  7. - **注解定义并使用对代码逻辑并没有任何影响。注解本质是通过反射获取注解对象以实现各种功能。比如写一个方法对字段长度进行校验。(不知道自动校验是如何实现的)**
  8. - **读取方法、字段和构造方法的Annotation和Class类似。**
  9. - **读取方法参数的Annotation就比较麻烦一点,因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示**
  10. ---
  11. - 判断某个注解是否存在于Class、Field、Method或Constructor,**class参数为注解类,返回值均为布尔值**
  12. - `Class.isAnnotationPresent(Class) `
  13. - `Field.isAnnotationPresent(Class)`
  14. - `Method.isAnnotationPresent(Class)`
  15. - `Constructor.isAnnotationPresent(Class)`
  16. - 读取一个注解,返回注解类对象,可能需要
  17. - `Class.getAnnotation(Class)` //读取类注解
  18. - `Field.getAnnotation(Class)` //读取字段注解
  19. - `Method.getAnnotation(Class)` //读取方法注解
  20. - `Constructor.getAnnotation(Class)` //读取构造方法注解
  21. - Annotation相关方法:
  22. - `Class s=annotation.annotationType();`//得到一个Annotation对象实际是哪个注解类
  23. ```java
  24. Class cls = Person.class;
  25. if (cls.isAnnotationPresent(Report.class)) {
  26. Report report = cls.getAnnotation(Report.class);
  27. ...
  28. }
public class Main {
    @Range(max = 90)
    public int b;
    public static void main(String[] args) throws NoSuchFieldException {
       Main main=new Main();
       Class cls=main.getClass();
       Field field=cls.getField("b");
        Range range= field.getAnnotation(Range.class);
        System.out.println(range.max());
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
    int min() default 10;
    int max() default 255;
}
public void hello(@NotNull @Range(max=5)String name, @NotNull String prefix) {}

// 获取Method实例:
Method m = ...
// 获取所有参数的Annotation:
Annotation[][] annos = m.getParameterAnnotations();
// 第一个参数(索引为0)的所有Annotation:
Annotation[] annosOfName = annos[0];
for (Annotation anno : annosOfName) {
    if (anno instanceof Range) { // @Range注解
        Range r = (Range) anno;
    }

    if (anno instanceof NotNull) { // @NotNull注解
        NotNull n = (NotNull) anno;
    }
}