• 注解(annotation)概述
    • 注解可以标识代码,或是为代码提供元数据
      • 有成员变量的注解称为元数据
      • 没有成员变量的注解称为标记
    • 注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在。
    • 注解本身没有任何作用,只有被外部程序/JVM解析时才会有作用
    • Java语言中的类、方法、变量、参数、包都可以被注解。

1 注解架构

d299de8d5eecbd43591209b61be88a33_28123151-d471f82eb2bc4812b46cc5ff3e9e6b82.jpg

  • 架构图的左侧表示Annotation接口的组成,架构图的右侧则是继承自Annotation接口的常用注解
  • 注解的本质是Annotation接口的子接口,所有注解都继承自Annotation接口
  • Annotation接口与1个RetentionPolicy关联,并且与1~n个ElementType关联

2 Annotation接口

  • java.lang.annotation.Annotation定义

    1. package java.lang.annotation;
    2. public interface Annotation {
    3. boolean equals(Object obj);
    4. int hashCode();
    5. String toString();
    6. Class<? extends Annotation> annotationType();
    7. }
  • java.lang.annotation.ElementType定义

    1. package java.lang.annotation;
    2. public enum ElementType {
    3. TYPE, //类、接口(包括注释类型)或枚举声明
    4. FIELD, //字段声明(包括枚举常量
    5. METHOD, //方法声明
    6. PARAMETER, //参数声明
    7. CONSTRUCTOR, //构造方法声明
    8. LOCAL_VARIABLE, //局部变量声明
    9. ANNOTATION_TYPE, //注释类型声明
    10. PACKAGE //包声明
    11. }
  • java.lang.annotation.RetentionPolicy定义

    1. package java.lang.annotation;
    2. public enum RetentionPolicy {
    3. SOURCE, //Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
    4. CLASS, //编译器将Annotation存储于类对应的.class文件中。默认行为
    5. RUNTIME //编译器将Annotation存储于class文件中,并且可由JVM读入
    6. }
  • Annotation与ElementType、RetentionPolicy

每1个Annotation对象,都会有唯一的RetentionPolicy属性以及1~n个ElementType属性。
ElementType

  • ElementType是Enum枚举类型,它用来指定Annotation可以修饰的类型,

当Annotation与某个ElementType关联时,就意味着Annotation有了某种用途。

  • 例如,若一个Annotation对象是METHOD类型,则该Annotation只能做作用于方法。

RetentionPolicy

  • RetentionPolicy是Enum枚举类型,它用来指定Annotation保留、存在的阶段,即保留策略。
  • 若Annotation的保留策略SOURCE,则意味着Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了。
    • 例如@Override注解就是一个SOURCE保留策略的注解。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法,并且在编译期间会进行语法检查。编译器处理完后,@Override就不存在了。
  • 若Annotation的保留策略为CLASS,则意味着编译器将Annotation存储于类对应的.class文件中,但运行时无法获得

CLASS是Annotation默认的保留策略

  • 若Annotation的保留策略为RUNTIME,则意味着编译器将Annotation存储于对应的.class文件中,并且在运行时可以通过反射获取到

3 具体的注解

1 Java内置注解

  • Java内置有7个注解,其中3个在java.lang包中,4个在java.lang.annotation包中。

7个注解可以分为两类

  1. 作用在代码的注解
  • @Override
  • @Deprecated
  • @SuppressWarnings
  • @SafeVarargs
  • @FunctionalInterface
  1. 作用在注解的注解 / 元注解

元注解是注解的注解,作用在其他注解的定义中

  • @Retention
  • @Documented
  • @Target
  • @Repeatable
  • @Inherited
  • 作用在代码的注解含义表 | 注解 | 含义 | | —- | —- | | @Override | 修饰目标:方法
    功能:标识该方法是重写方法。如果发现包含该方法的类的父类或实现的接口中并没有该方法时,会报编译错误。 | | @Deprecated | 修饰目标:类、方法、参数、构造器、实例字段等
    功能:标记被修饰的目标为过时的。如果使用会报编译警告 | | @SuppressWarnngs | 修饰目标:类、方法、参数、构造器、实例字段等
    功能:指示编译器忽略注解中声明的警告 | | @FunctionalInterface | 修饰目标:接口
    功能:JDK8开始支持,标识接口为函数式接口 |
  • 元注解含义表

所有元注解的ElementType为ANNOTATION_TYPE

注解 含义
@Documented 标识其修饰的注解包含到Javadoc中。
定义注解时,@Documented可有可无;若没有定义,则注解不会出现在Javadoc。
@Target 用来指定注解的类型属性,即ElementType
定义注解时,@Target可有可无。若有@Target,则该注解只能用于它所指定的地方;若没有@Target,则该注解可以用于任何地方
@Retention 用来指定注解的保留策略,即RetentionPolicy
@Repeatable 标识注解可以多次使用,每次使用的功能不同
需要一个参数,表示用来保存被修饰的注解的容器

2 定义注解

  • 自定义注解实例 ```java @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation{

}

  1. - 上面的作用是定义一个注解,它的名字是MyAnnotation。定义了MyAnnotation之后,我们可以在代码中通过@MyAnnotation来使用它。
  2. - **@interface用于声明一个注解**,同时使自定义的注解自动继承子Annotation接口。
  3. - @Documented@Target@Retention都是来修饰MyAnnotation
  4. - **JDK注解实例**
  5. ```java
  6. package java.lang;
  7. import java.lang.annotation.*;
  8. @Target(ElementType.METHOD)
  9. @Retention(RetentionPolicy.SOURCE)
  10. public @interface Override {
  11. }

4 注解的属性

  • 注解中没有方法,只有成员变量

1 定义注解的属性

  • 注解的属性只能是以下类型

    • 基本数据类型
    • String
    • Enum
    • Annotation
    • Class
    • 以上类型的一维数组
  • 注解声明属性的语法格式如下

类型 变量名() [default 默认值];

  • 变量名后需要加小括号
  • 可以在小括号后为变量指定默认值,如不指定,则使用注解时必须为其属性赋值
  • 实例
    1. package org.springframework.beans.factory.annotation;
    2. import ...
    3. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    4. @Retention(RetentionPolicy.RUNTIME)
    5. @Documented
    6. public @interface Autowired {
    7. boolean required() default true;
    8. }
    1. package java.lang.annotation;
    2. @Documented
    3. @Retention(RetentionPolicy.RUNTIME)
    4. @Target(ElementType.ANNOTATION_TYPE)
    5. public @interface Target {
    6. ElementType[] value();
    7. }

2 使用带有属性的注解

  • 使用规则
    1. 如果定义的注解含有属性,那在使用注解时必须在注解后加上小括号并指定属性值

指定属性值的形式为**属性名1=属性值1, 属性名2=属性值2, ...**
也可以不指定属性名,但是传入的属性值的顺序必须和注解中定义属性的顺序一致

  1. 如果注解只有一个属性,直接写属性值即可,因为属性值与属性名的对照顺序一定不会错
  2. 注解属性定义中指定了默认值的参数可以不指定值,但没有的一定要指定值
  • 实例
    1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})

3 获取注解的属性

  • 获取规则
    1. 如果想要获取注解的属性,那么注解的保留策略必须指定为RetentionPolicy.RUNTIME
    2. 需要通过反射机制获取反射的属性,主要涉及以下三个Class类实例方法 ```java /*判断Class对象是否存在Annotation对象/ public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { return GenericDeclaration.super.isAnnotationPresent(annotationClass); }

/*获取Annotation对象/ public A getAnnotation(Class annotationClass) { Objects.requireNonNull(annotationClass);

  1. return (A) annotationData().annotations.get(annotationClass);

} /*获取所有Annotation对象数组/
public Annotation[] getAnnotations() { return AnnotationParser.toArray(annotationData().annotations); }

  1. - **获取注解属性实例**
  2. ```java
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Target(ElementType.TYPE)
  5. @interface MyTestAnnotation {
  6. String name() default "cui";
  7. int age() default 18;
  8. }
  9. @MyTestAnnotation(name = "father", age = 50)
  10. class Father {
  11. }
  12. public class MyClass {
  13. public static void main(String[] args) {
  14. Class<Father> fatherClass = Father.class;
  15. boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
  16. if (annotationPresent) {
  17. MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
  18. System.out.println(annotation.name());
  19. System.out.println(annotation.age());
  20. }
  21. }
  22. }