Java注解 (元数据)

注解的概念及作用

  • 从JDK1.5开始,Java支持在源文件中嵌入补充信息,这类信息被称为注解(annotation).注解不会改变程序的动作,因此也就不会改变程序的语义,但是在开发和部署期间,各种工具可以使用这类信息,例如: 源代码生成器可以处理注解! 注: 如果没有注解信息处理流程(如:通过反射查询注解),则注解毫无意义 !

  • 说明: 其实术语元数据(metadata),但是注解这个名称更具有描述性并且更常用.

声明注解

  • 注解是一种代码级别的说明.它与,接口,枚举在同一个层次,它可以声明在,,字段,方法,局部变量,方法参数等的前面,用来对这些元素进行说明,注释..

注解的分类

  1. 特殊的注解 : 标记注解,单成员注解.
  2. 内置注解.
  3. 类型注解.
  4. 重复注解.

内置注解

内置注解分类

  1. java.lang.annotation包下的注解分别是 : @Retention,@Documnetd,@Target,@Inherited
  • 以上四个注解被称为元注解,其作用是负责注解其它的注解,用来提供对其它annotation类型做说明.
  1. java.lang包下注解分别是 : @Override,@Deprecated,@FunctionalInterface,@SafeVarargs,@SuppressWarnings
  • 注意: JDK8java.lang.annotation中添加了RepeatableNative注解.其中Repeatable支持重复注解,Native用于注解本机代码可以访问的域变量.

内置注解 : 非元注解

  • @SuppressWarnings注解使用详解如下图所示
  1. @Override,@Deprecated,@SafeVarargs,SuppressWarnings程序示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    package pers.huangyuhui.annotation.built_in_annotation;

    import java.util.ArrayList;

    /**
    * @ClassName: Built_in_annotations
    * @Description: 非元注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 9:36:20 AM
    *
    */
    public class Built_in_annotations<T> {

    @SuppressWarnings("unused")
    private T[] args;

    // Type safety: Potential heap pollution via varargs parameter args..
    // @SafeVarargs: 为标记注解,只能用于方法和构造方法,指示没有发生与可变长度参数相关的不安全动作.
    @SafeVarargs
    public Built_in_annotations(T... args) {
    this.args = args;
    }

    // @SafeVarargs: 该注解只能用于varargs方法或者声明为static或final的构造方法.
    @SafeVarargs
    public final void safeVarargsTest(T... args) {

    for (@SuppressWarnings("unused")
    T t : args) {
    System.out.println(args);
    }
    }

    // @Override: 用于确保超类方法被真正地重写,而不是简单地重载.
    @Override
    public String toString() {
    return super.toString();
    }

    // @Deprecated: 为标记注解,用于指示声明是过时的,并且已经被更新到的形式取代.
    @Deprecated
    public void deprecatedTest() {

    }

    // @SuppressWarnings: 用于抑制一个或多个编译器可能会报告的警告.使用以字符串形式表示的名称来指定要被抑制的警告.
    @SuppressWarnings("rawtypes")
    public void supressWarningsTest() {

    @SuppressWarnings("unused")
    ArrayList arrayList = new ArrayList<>();
    }

    }

  2. @FunctionalInterface程序示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    /**
    * @ClassName: functionalInterface
    * @Description: `学习@functionalInterface注解`
    * @author: HuangYuhui
    * @date: Apr 14, 2019 10:39:46 AM
    *
    */
    //`Java8`为函数式接口引入了一个新注解`@FunctionalInterface`,其主要用于编译错误检查,
    //加上该注释后当你写的接口不符合函数式定义时,编译器就会报错 !
    //注意: 是否添加`@FunctionalInterface`对于接口是不是函数式接口没有影响,
    //该注释只是提醒`编译器`去检查该接口是否仅仅包含一个抽象方法.
    @FunctionalInterface
    interface functionalInterface {

    // 注意: 函数式接口中只能有一个抽象方法 !
    void myLambda(String message);

    // 函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,
    //所以是符合函数式接口的定义的.
    default void defaultMethod() {
    // method body..
    }

    // 函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,
    //所以是符合函数式接口的定义的.
    static void staticMethod() {
    // method body..
    }

    // 函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,
    // 不被当成是抽象方法(虽然它们是抽象方法).因为任何一个函数式接口的实现,默认都继承了Object类,
    //其包含了来自java.lang.Object里对这些抽象方法的实现!
    @Override
    boolean equals(Object object);

    }

    //使用`@FunctionalInterface`注解的函数式接口继而可以使用`Lambda`表示式来表示该接口的一个实现.
    //(Java8 之前一般是使用匿名类实现的).
    //functionalInterface f = message->System.out.println();

内置注解 : 元注解

  • 使用@Target,@Retention自定义注解
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    /**
    * @ClassName: MyTarget_annotation
    * @Description: 自定义注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 11:12:54 AM
    *
    */

    @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.LOCAL_VARIABLE,
    ElementType.CONSTRUCTOR })//用于指定可以应用注解的声明类型,被设计为只能注解其它注解.
    @Retention(RetentionPolicy.CLASS)//指定保留策略.
    public @interface MyTarget_annotation {

    int id() default -1; //使用默认值

    String name();

    String[] address() default { "null" };

    }

  • 使用自定义注解
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import pers.huangyuhui.annotation.myannotation.MyTarget_annotation;

    /**
    * @ClassName: TargetTest
    * @Description: 使用自定义注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 11:38:23 AM
    *
    */
    @MyTarget_annotation(name = "")
    public class TargetTest {

    // 由于声明的注解类型包含 : `CONSTRUCTOR`, 所以可以在构造方法前使用.
    @MyTarget_annotation(name = "")
    public TargetTest() {
    // TODO Auto-generated constructor stub
    }

    // 由于声明的注解类型包含 : `FIELD`, 所以可以在域变量前使用.
    @MyTarget_annotation(name = "")
    private String s;

    // 由于声明的注解类型包含 : `METHOD`, 所以可以在方法前使用.
    @MyTarget_annotation(id = 1, name = "YUbuntu0109", address = "my address") // 显式的定义注解值
    public void testMethod() {

    // 由于声明的注解类型包含 : `LOCAL_VARIABLE`, 所以可以在局部变量前使用.
    @SuppressWarnings("unused")
    @MyTarget_annotation(name = "")
    int i;
    }

    }

使用反射获取注解

  1. 通过反射获取方法关联的注解
  • 首先自定义一个注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package pers.huangyuhui.annotation.myannotation;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    /**
    * @ClassName: MyAnnotation
    * @Description: 自定义注解,用于通过反射获取注解信息的测试
    * @author: HuangYuhui
    * @date: Apr 14, 2019 5:52:05 PM
    *
    */

    @Retention(RetentionPolicy.RUNTIME)//指定保留策略
    @Target({ ElementType.METHOD, ElementType.TYPE })
    public @interface MyAnnotation {

    int id() default 1;

    String name() default "default value";
    }

  • 通过反射获取自定义注解信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import java.lang.reflect.Method;

    import pers.huangyuhui.annotation.myannotation.MyAnnotation;

    /**
    * @ClassName: getAnnotationInfo
    * @Description: 通过反射获取方法关联的注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 5:45:32 PM
    *
    */
    public class getAnnotationInfo {

    @MyAnnotation(name = "Hello annotation !")
    public void fun(double d, String s) {
    }

    public static void main(String[] args) {
    getAnnotationInfo getInfo = new getAnnotationInfo();

    // 获取Class对象
    Class<?> class1 = getInfo.getClass();
    try {
    // 通过调用Class泛型类提供的`getMethod`方法获取与方法相关的信息
    Method method = class1.getMethod("fun", double.class, String.class);
    // 通过调用`getAnnotation`获取与对象相关联的特定注解
    MyAnnotation annotatioin = method.getAnnotation(MyAnnotation.class);

    System.out.println("id : " + annotatioin.id() + "\nname : " + annotatioin.name());

    } catch (NoSuchMethodException | SecurityException e) {
    e.printStackTrace();
    }

    }

    }
  • 程序运行结果如下

    1
    2
    id : 1 
    name : Hello annotation !
  1. 通过反射获取所有注解
  • 首先自定义一个注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package pers.huangyuhui.annotation.myannotation;

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

    /**
    * @ClassName: MyAnnotation2
    * @Description: 自定义注解,用于反射获取注解信息的测试
    * @author: HuangYuhui
    * @date: Apr 14, 2019 6:09:15 PM
    *
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation2 {

    String description();
    }
  • 通过反射获取自定义注解信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package pers.huangyuhui.annotation.reflect;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;

    import pers.huangyuhui.annotation.myannotation.MyAnnotation;
    import pers.huangyuhui.annotation.myannotation.MyAnnotation2;

    /**
    * @ClassName: getAllAnnotationInfo
    * @Description: 通过反射机制获取所有注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 6:10:29 PM
    *
    */

    @MyAnnotation()
    @MyAnnotation2(description = "Test class")
    public class getAllAnnotationInfo {

    @MyAnnotation(id = 1, name = "Hello annotation !")
    @MyAnnotation2(description = "Test method")
    public void fun() {

    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    getAllAnnotationInfo allAnnotationInfo = new getAllAnnotationInfo();

    // 通过`getAnnoations`获取该类所有注解信息
    Annotation annotation[] = allAnnotationInfo.getClass().getAnnotations();

    System.out.println("`getAllAnnotationInfo`类的所有注解 : ");
    for (Annotation info : annotation) {
    System.out.println(info);
    }

    System.out.println();
    // 通过调用Class泛型类提供的`getMethod`方法获取与方法相关的信息
    Method method = allAnnotationInfo.getClass().getMethod("fun");
    // 通过调用`getAnnotation`获取与对象相关联的特定注解
    annotation = method.getAnnotations();

    System.out.println("方法`fun()`的所有注解 : ");
    for (Annotation info : annotation) {
    System.out.println(info);
    }

    }

    }
  • 程序运行结果如下

    1
    2
    3
    4
    5
    6
    7
    8
    `getAllAnnotationInfo`类的所有注解 : 
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="default value", id=1)
    @pers.huangyuhui.annotation.myannotation.MyAnnotation2(description="Test class")

    方法`fun()`的所有注解 :
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="Hello annotation !", id=1)
    @pers.huangyuhui.annotation.myannotation.MyAnnotation2(description="Test method")

标记注解

  • 编写示例程序来判断标记注解是否存在
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.reflect.Method;

    /**
    * @ClassName: TagTest
    * @Description: 标记注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 6:57:26 PM
    *
    */

    @Retention(RetentionPolicy.RUNTIME)
    @interface MyMarker_annotation {

    }

    public class MarketTest {

    @MyMarker_annotation
    public static void fun() {
    MarketTest marketTest = new MarketTest();

    try {

    // 通过调用Class泛型类提供的`getMethod`方法获取与该方法相关的信息
    Method method = marketTest.getClass().getMethod("fun");
    // 通过使用`AnnotatedElement`接口的`isAnnotationPresent`方法来确定自定义注解中是否存在`标记注解`
    if (method.isAnnotationPresent(MyMarker_annotation.class)) {
    System.out.println("存在标记注解 !");
    }
    } catch (Exception e) {
    System.err.println("Method not found !");
    }
    }

    public static void main(String[] args) {
    fun();
    }
    }

  • 程序运行结果如下
    1
    存在标记注解 !
  • 说明 : 因为标记注解没有成员,所以应用标记注解时,后面不需要有圆括号.如果提供空的圆括号,也不会报错,但不是必需的.类似地,对于所有成员都使用默认值(default)的注解也可以没有圆括号.

重复注解

  • 要创建重复注解,必须创建容器注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package pers.huangyuhui.annotation.myannotation;

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

    /**
    * @ClassName: MyReapeatable_annotation
    * @Description: `重复注解`的容器注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 8:08:21 PM
    *
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyReapeatable_annotation {

    // `value`成员指定的是重复注解类型的数组
    MyAnnotation[] value();
    }
  • 创建重复注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package pers.huangyuhui.annotation.myannotation;

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

    /**
    * @ClassName: MyAnnotation
    * @Description: 重复注解
    * @author: HuangYuhui
    * @date: Apr 14, 2019 5:52:05 PM
    *
    */

    //指定`@Repeatable`注解的`value`成员值为容器注解 : `MyReapeatable_annotation`
    @Repeatable(MyReapeatable_annotation.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {

    int id() default 1;

    String name() default "default value";
    }
  • 通过反射获取重复注解信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package pers.huangyuhui.annotation.myannotation_test;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;

    import pers.huangyuhui.annotation.myannotation.MyAnnotation;

    /**
    * @ClassName: RepeatTest
    * @Description: 通过反射获取`重复注解`的信息
    * @author: HuangYuhui
    * @date: Apr 14, 2019 7:58:54 PM
    *
    */
    public class RepeatTest {

    @MyAnnotation
    @MyAnnotation(id=2)
    @MyAnnotation(id=3,name="YUbuntu0109")
    public static void testMethod() {

    RepeatTest repeatTest = new RepeatTest();

    try {

    // 获取Class对象
    Class<?> class1 = repeatTest.getClass();

    // 通过调用Class泛型类提供的`getMethod`方法获取与该方法相关的信息
    Method method = class1.getMethod("testMethod");

    // `getAnnotation`用于获取与指定对象关联的特定注解
    // Annotation annotation = method.getAnnotation(MyReapeatable_annotation.class);
    // System.out.println(annotation);

    // 使用`getAnnotationsByType`来获取`MyAnnotation`重复注解
    Annotation[] annotations = method.getAnnotationsByType(MyAnnotation.class);
    for (Annotation info : annotations) {
    System.out.println(info);
    }

    } catch (NoSuchMethodException e) {
    System.err.println("Error : Method not found !");
    }
    }

    public static void main(String[] args) {
    testMethod();
    }
    }

  • 程序运行结果如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    //程序运行结果 :
    @pers.huangyuhui.annotation.myannotation.MyReapeatable_annotation(value={

    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="default value", id=1),
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="default value", id=2),
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="YUbuntu0109", id=3)

    })

    //程序运行结果(使用`getAnnotationsByType`来获取`MyAnnotation`重复注解) :
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="default value", id=1)
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="default value", id=2)
    @pers.huangyuhui.annotation.myannotation.MyAnnotation(name="YUbuntu0109", id=3)