Earth Guardian

You are not LATE!You are not EARLY!

0%

Java 注解 Annotation

Java 注解 Annotation:代码中的特殊标记,这些标记可以在编译、类加载、运行时读取,并执行相应的解析处理。注解本身并不影响代码以及运行结果,只有在解析这些注解后才会生效。

Annotation 很像一个接口(反编译后发现注解就是一个接口),通过 @interface 修饰来,同一个文件中只能有一个 Annotation 的定义。

元注解

元注解 Meta Annotation,用来修饰其他 Annotation ,即表示“其他注解”的注解。

@Retention

用于指定被修饰 Annotation 能保留多长时间。包含三个 RetentionPolicy 类型的 value 成员变量:

  • RetentionPolicy.SOURCE
    注解只保留在源代码中,编译时直接丢弃这些注解。
  • RetentionPolicy.CLASS
    编译器将注解记录在 class 文件中,在运行时 JVM 不再保留注解。它也是@Retention 的默认值。
  • RetentionPolicy.RUNTIME
    编译器不仅将注解记录在 class 文件中,运行时 JVM 也会保留注解。也就是说程序想通过反射来获取注解信息,则必须使用 RUNTIME 类型。

使用 @Retention 的示例:

1
2
3
4
5
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Testable{}

@Retention(RetentionPolicy.CLASS)
public @interface Testable{}

@Target

只能用来修饰一个 Annotation 的定义,用于指定被修饰的 Annotation 能够修饰哪些程序元素。包含如下 ElementType 类型的 value 成员变量:

  • ElementType.TYPE:指定该注释可以修饰类、接口或枚举
  • ElementType.FIELD:指定该注解只能修饰成员变量
  • ElementType.METHOD:指定该注解只能修饰方法
  • ElementType.PARAMETER:指定该注解只能修饰参数
  • ElementType.CONSTRUCTOR:指定该注解只能修饰构造器
  • ElementType.LOCAL_VARIABLE:指定该注解只能修饰局部变量(不能通过反射解析,class 文件并不保留局部变量注解。只能通过源码在编译前解析)
  • ElementType.ANNOTATION_TYPE:指定该注解只能修饰注解
  • ElementType.PACKAGE:指定该注解只能修饰包定义
  • ElementType.TYPE_PARAMETER:指定该注解只能参数化类型
  • ElementType.TYPE_USE:指定该注解可以修饰上面几种任意类型

如果需要指定多个 ElementType 类型的成员变量,使用数组赋值。

1
2
3
4
5
6
7
// 修饰单个元素
@Target(ElementType.METHOD)
public @interface Testable {}

// 修饰多个元素
@Target(value = {ElementType.METHOD, ElementType.FILED})
public @interface Testable2 {}

@Documented

修饰其他 Annotation 时,通过 javadoc 工具提取文档会保留这个 Annotation 信息。

1
2
3
4
5
6
7
8
9
10
11
12
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Testable{}

public class Test{
/**
* This is testable method.
*/
@Testable
public void myJavaDocInfo(){...}
}

javadoc 生成文档时,myJavaDocInfo 方法文档会保留 @Testable 信息。如果 Testable 的元注解中去掉 @Documented,则生成的文档不会保留 @Testable 信息。

@Inherited

修饰其他 Annotation 时,表示该注解具有继承性。比如 @A@Inherited 注解,@A 注解父类,则其所有子类自动被 @A 注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. Annotation
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable {
}

// 2. Father
@Inheritable
public class Father {
}

// 3. Son
public class Son extends Father {
public static void main(String[] args) {
System.out.println(Son.class.isAnnotationPresent(Inheritable.class));
}
}

// 4. Result
true

运行结果显示,虽然 Son 并没有被 Inheritable 注解,但是自动从 Father 那继承了该注解。

@Repeatable

Java 8 加入的新特性,表示注解中相同元素的值可以取多个。比如某个类需要扮演多个角色:医生、护士、警察。在 Java 8 之前是不允许有相同注解重复的。

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
// 1. 定义角色注解  
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
String name() default "role";
}

// 2. 定义角色数组,用来存储多个角色
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Roles {
Role[] value();
}

// 3. 回过头来,将 Role 定义为可重复
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Roles.class)
public @interface Role {
String name() default "role";
}

// 4. 使用示例
// 单独重复使用
@Role(name = "doctor")
@Role(name = "nurse")
public static class RepeatAnn{}

// 使用数组
@Roles({@Role(name="doctor"),
@Role(name="policeman")})
public static class Annotations{}

元注解的源码

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// 1. Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}

public enum RetentionPolicy {
//Annotations are to be discarded by the compiler.
SOURCE,

/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,

/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}

// 2. Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}

public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,

/** Field declaration (includes enum constants) */
FIELD,

/** Method declaration */
METHOD,

/** Formal parameter declaration */
PARAMETER,

/** Constructor declaration */
CONSTRUCTOR,

/** Local variable declaration */
LOCAL_VARIABLE,

/** Annotation type declaration */
ANNOTATION_TYPE,

/** Package declaration */
PACKAGE,

/**
* Type parameter declaration
*
* @since 1.8
* @hide 1.8
*/
TYPE_PARAMETER,

/**
* Use of a type
*
* @since 1.8
* @hide 1.8
*/
TYPE_USE
}

// 3. Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

// 4. Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

// 5. Repeatable
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}

自定义注解

自定义注解使用 @interface 作为关键字。

定义

格式,注意成员变量后有个双括号 (),以 ; 结尾:

1
2
3
访问控制符 @interface 注解名称{
返回类型 成员变量名() default 默认值;
}
  • 标记 Annotation
    如果没有定义成员变量,则该注解被称为标记。这种注解仅仅利用自身存在来给我们提供信息,比如 @Override 等等。
    1
    2
    @Target(ElementType.PARAMETER)
    public @interface TestPara{}
  • 元数据 Annotation
    包含成员变量,他们可以接受更多的元数据,每个成员变量可以有默认值,使用 default 关键字。
    1
    2
    3
    4
    5
    6
    7
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MetaAnnotation {
    String author();
    String date();
    int currentVersion() default 1;
    String[] reviewer();
    }

使用方法

格式:

1
2
3
4
@自定义注解(成员变量1=指定值1,
成员变量2=指定值2
成员变量数组={指定值3, 指定值4})
被注解程序元素

成员变量是以名字和值 name=value 形式成对出现的,如果只有一个,可以省略成员变量名称。被注解元素,是依据 @Target 指定的类型。

1
2
3
4
5
6
7
8
9
10
11
12
@MetaAnnotation(
author = "xmt",
date = "2018.4.27",
currentVersion = 2,
reviewer = {"a", "b", "c"}
)
@Role("nurse") // 忽略名称
@Roles(value = {@Role(name = "doctor"), @Role(name = "police")}) // 名字值对
public class TestAnnotation {
private void testPara(@TestPara String para){...}
...
}

Java 预置的基本注解

@Override

@Override 只能注解方法,用来表示方法重写。编译器会检查该方法,保证父类要包含一个被重写的方法,否则会编译报错。

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated

@Deprecated 用于注解方法,类,字段等,表示该程序元素已过时。当其他程序调用时,编译器会给出警告。

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnings

@SuppressWarnings 注解表示取消显示指定的编译器警告。比如:unused, unchecked, all 等警告。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is <i>not</i> an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* <p>Compiler vendors should document the warning names they support in
* conjunction with this annotation type. They are encouraged to cooperate
* to ensure that the same names work across multiple compilers.
*/
String[] value();
}

@SafeVarargs

Java 7 新增的注解,参数安全类型注解,它的目的是提醒开发者不要用参数做一些不安全的操作。比如堆污染:把一个不带泛型的对象赋给一个带泛型的变量是,就会发生堆污染。

1
2
3
4
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

@FunctionalInterface

Java 8 新增的注解,表示函数式接口注解。
函数式接口 Functional Interface:就是只有一个方法的普通接口,使用 @FunctionalInterface 注解后,编译器会检查确保符合规范。

1
2
3
4
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

其他

注解是一种类型

注解是具体的类型,可以使用 instanceof 来判断,示例:if (annotation instanceof GET) {...}

注解的解析

  • 通过反射处理注解
    这种处理方式要求注解必须是 @Retention(RetentionPolicy.RUNTIME) 类型的。在运行时,通过反射来获取并解析注解。但是反射比较慢,所以需要考虑效率问题。
  • 编译时处理注释
    因为反射效率和性能问题,Java 还提供了编译时解析注解并自动生成代码。Java 7 之前可以使用 APT: Annotation Processing Tool 工具提取注解并解析后,生成对应的代码。但是 Java 7 开始降级移除了 APT 并采用 JSR 269: Pluggable Annotation Processing API,原因及说明见官网解释原因

参考文档