Java 注解

208 阅读4分钟

前言

在文章开始之前,你是否知道如何解答下列问题?

  1. 注解是什么?
  2. 注解有什么意义?
  3. 如何声明一个注解类型?
  4. 元注解是什么?
  5. 元注解 @Target@Retention 分别如何使用?
  6. 对于注解类型元素你都知道什么?
  7. 注解在不同保留级别下的应用场景有哪些?

下文将给出详细说明。

1. 注解概述

1.1 注解是什么?

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

元数据:描述数据的数据。

1.2 注解的作用或意义是什么?

单独的注解就就是一种注释,就如同超市中的标签一样。注解本身没有任何代码意义。

注解结合其他技术使用才有意义,比如反射、插桩等。

2. 注解声明

2.1 声明一个注解类型

注解的声明使用@interface关键字:

public @interface Liang {
}

Java 中所有注解默认实现Annotation接口:

package java.lang.annotation;

public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();

}

2.2 元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)

元注解是注解的注解

@Target

@Target:目标注解,标记另一个注解,以限制可以应用注解的 Java 元素类型,即指定注解可以写到什么地方。目标注解指定以下元素类型之一作为其值:

  • ElementType.ANNOTATION_TYPE:可以应用于注解类型。
  • ElementType.CONSTRUCTOR:可以应用于构造函数。
  • ElementType.FIELD: 可以应用于字段或属性。
  • ElementType.LOCAL_VARIABLE: 可以应用于局部变量。
  • ElementType.METHOD: 可以应用于方法级注解。
  • ElementType.PACKAGE: 可以应用于包声明。
  • ElementType.PARAMETER: 可以应用于方法的参数。
  • ElementType.TYPE: 可以应用于类。
//@Target(ElementType.TYPE) //元注解,Target作用在Liang上,此处指定Liang只能作用在类上面。
@Target({ElementType.TYPE,ElementType.FIELD}) // 可以同时指定多个地方,用在允许在类与类属性上标记该注解
@Retention(RetentionPolicy.SOURCE) //注解保留在源码中
public @interface Liang {

}

@Retention

@Retention:指定标记注解的存储方式,保留级别,注解的生命周期,注解可以存活到什么时候:

  1. RetentionPolicy.SOURCE: 标记的注解仅保留在源码级别中,并被编译器忽略。
  2. RetentionPolicy.CLASS: 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
  3. RetentionPolicy.RUNTIME: 标记的注解由 JVM 保留,因此运行时环境可以使用它。

注:可以在字节码中查看。

2.3 注解类型元素

在使用注解时,允许传递参数,可以定义可选的默认值。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Liang {
    String value();  // String 类型的 value 元素
    String apple() default "a"; // 有默认值

}
// 注解的使用
@Liang(value = "1", apple = "11")
public class MyClass {
    ...
}

注:注解里面有元素,default 后可以设置默认值,设置默认值时,使用注释可以不传参,没有默认值时,必须传参。

这里注意,value 元素较特殊,若注解中仅有 value 一个元素时,传参时可以不用声明。

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.SOURCE)
public @interface Liang {
    String value(); 
}
//注解的使用,声明首字母
@Liang("11")
public class MyClass {}

若改成其他元素,使用时需声明。注解中有多个元素时,即使是 value 元素,也需要声明。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Liang {
    String value();
    String string();

}

@Liang(value = "1", s = "11")

3. 注解的应用场景

根据注解的保留级别不同,对应的使用场景也不同:由注解的三个不同保留级别可知,注解作用于:SOURCE(源码)、CLASS(字节码)与RUNTIME(运行时)。

级别技术说明
源码APT注解处理器/IDE语法检查在编译期能够获取注解与注解声明的类包括类中的所有的成员信息,一般用于生成额外的辅助类
字节码字节码增强在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
运行时反射在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。

对于不同的保留级别,注解有不同的应用:

  1. RetentionPolicy.SOURCE

  2. RetentionPolicy.CLASS

    • 字节码增强:在字节码中写代码
  3. RetentionPolicy.RUNTIME

    • 反射

注解在不同应用场景的具体使用待补充...