Java注解
1. 概述、自定义注解
从JDK 5 开始,Java 增加了对元数据(MetaData)的支持,也就是 Annotation(即注解,也被翻译为注释)。本章所介绍的 Annotation,其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解, 程序开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
举例: @Override、@Test等等,作用是:让其他程序根据注解信息来决定怎么执行该程序。注解可以使用在类上、构造器上、方法上、成员变量上、参数上等位置。
1.1 注意
Annotation 是一个接口,程序可以通过反射来获取指定程序元素的 Annotation 对象, 然后通过 Annotation 对象来取得注解里的元数据。读者需要注意本章中使用 Annotation 的地方,有的 Annotation 指的是 java.lang.Annotation
接口,有的指的是注解本身。
Annotation 能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是,Annotation 不影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一地执行。如果希望让程序中的 Annotation 在运行时起一定的作用,只有通过某种配套的工具对 Annotation 中的信息进行访问和处理, 访问和处理 Annotation 的工具统称 APT (Annotation Processing Tool)。
2. 自定义注解
自定义注解的格式
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RecordOperate {
String desc() default "";
Class<? extends Convert> convert();
}
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 定义注解
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略
@Target(ElementType.METHOD) // 指定注解的使用目标
public @interface MyAnnotation {
String value() default "default value"; // 特殊属性
int number() default 0;
}
public class MyClass {
@MyAnnotation(value = "test value", number = 10)
public void myMethod() {
System.out.println("This is a method with custom annotation.");
}
}
package annotation;
import java.lang.reflect.Method;
public class AnnotationProcessor {
public static void main(String[] args) {
try {
// 获取MyClass的Class对象
Class<MyClass> myClassClass = MyClass.class;
// 获取所有方法
Method[] methods = myClassClass.getDeclaredMethods();
// 遍历所有方法
for (Method method : methods) {
// 检查方法是否有MyAnnotation注解
if (method.isAnnotationPresent(MyAnnotation.class)) {
// 获取注解
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
// 打印注解的值
System.out.println("Method: " + method.getName());
System.out.println("Value: " + annotation.value());
System.out.println("Number: " + annotation.number());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.1 注解的原理
3. JDK中的元Annotaion
JDK除了在 java.lang
下提供了5个基本的 Annotation 之外,还在 java.lang.annotation
包下提供了6 个 Meta Annotation(元 Annotation)
,其中有5个元 Annotation 都用于修饰其他的 Annotation 定义。其中@Repeatable
专门用于定义 Java 8新增的重复注解。
3.1 @Retention
@Retention
只能用于修饰 Annotation 定义,用于指定被修饰的 Annotation
可以保留多长时间, @Retention
包含一个 RetentionPolicy
类型的 value 成员变量,所以使用@Retention 时必须为该 value 成员变量指定值。
value 成员变量的值只能是如下三个。
- RetentionPolicy.CLASS:编译器将把 Annotation记录在class文件中。当运行Java程序时,JVM 不可获取 Annotation信息。这是默认值。
- RetentionPolicy.RUNTIME:编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM也可获取Annotation信息,程序可以通过反射获取该 Annotation 信息。
- RetentionPolicy.SOURCE: Annotation 只保留在源代码中,编译器直接丢弃这种 Annotation。
如果需要通过反射获取注解信息,就需要使用 value 属性值为RetentionPolicy.RUNTIME
的@Retention
。使用@Retention
元Annotation
可采用如下代码为 value 指定值。
//定义下面的 Testable Annotation 保留到运行时
@Retention (value = RetentionPolicy.RUNTIME)
public @interface Testablel{}
3.2 @Target
@Target
也只能修饰一个Annotation定义,它用于指定被修饰的 Annotation 能用于修饰哪些程序单元。@Target
元 Annotation 也包含一个名为 value 的成员变量,该成员变量的值只能是如下几个。
- ElementType.ANNOTATION_TYPE:指定该策略的 Amnotation 只能修饰 Amotation。
- ElementType.CONSTRUCTOR:指定该策略的 Annotation 只能修饰构造器。
- ElementType.FIELD:指定该策略的 Annotation 只能修饰成员变量。
- ElementType.LOCAL_VARIABLE:指定该策略的 Annotation 只能修饰局部变量。
- ElementType.METHOD:指定该策略的 Annotation 只能修饰方法定义。
- ElementType.PACKAGE:指定该策略的 Annotation 只能修饰包定义。
- ElementType.PARAMETER:指定该策略的 Annotation 可以修饰参数。
- ElementType.TYPE:指定该策略的 Annotation 可以修饰类、接口(包括注解类型)或枚举定义。
与使用 @Retention
类似的是,使用@Target
也可以直接在括号里指定 value 值,而无须使用 name-value 的形式。如下代码指定@ActionListenerFor Annotation 只能修饰成员变量。
3.3 使用@Documented
@Documented
用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档,如果定义 Annotation 类时使用了@Documented
修饰,则所有使用该 Annotation 修饰的程序元素的 API 文档中将会包含该 Annotation 说明。
下面代码定义了一个 Testable Annotation,程序使用@Documented
来修饰@Testable Annotation 定义, 所以该 Annotation 将被 javadoc 工具所提取。
上面代码中的粗体字代码指定了 javadoe 工具生成的 API文档将提取@Testable 的使用信息。
下面代码定义了一个 MyTest 类,该类中的 info()方法使用了@Testable 修饰。
3.4 使用@Inherited
@Inherited 元 Annotation 指定被它修饰的 Annotation 将具有继承性——如果某个类使用了@Xxx注解(定义该 Annotation 时使用了@Inherited 修饰)修饰,则其子类将自动被@Xxx 修饰。
或者说:@Inherited 是 Java 中的一个注解,用于指示某个注解类型可以被自动继承。如果一个类使用了带有 @Inherited 注解的注解,那么它的子类将会自动继承该注解。这个注解仅适用于类(Class),不适用于方法、字段、参数等其他元素。
@Inherited 注解的使用示例
- @Inherited 是一个元注解(用于注解其他注解)。
- 它位于 java.lang.annotation 包中。
- 它只能用于注解类型上,不能用于其他元素(如方法、字段等)。
- 当带有 @Inherited 注解的注解被使用在一个类上时,该注解将会被其子类继承。
3.4.1 @Inherited 注解的使用示例
下面是一个简单的例子,演示如何使用 @Inherited
注解。
- 创建一个带有 @Inherited 的自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyInheritedAnnotation {
String value() default "Inherited Annotation";
}
- 使用 @MyInheritedAnnotation 注解父类
@MyInheritedAnnotation
public class ParentClass {
// Parent class code
}
- 创建一个继承父类的子类
public class ChildClass extends ParentClass {
// Child class code
}
- 测试继承效果
import java.lang.annotation.Annotation;
public class InheritedAnnotationDemo {
public static void main(String[] args) {
// 获取父类上的注解
Annotation[] parentAnnotations = ParentClass.class.getAnnotations();
System.out.println("Annotations on ParentClass:");
for (Annotation annotation : parentAnnotations) {
System.out.println(annotation);
}
// 获取子类上的注解
Annotation[] childAnnotations = ChildClass.class.getAnnotations();
System.out.println("\nAnnotations on ChildClass:");
for (Annotation annotation : childAnnotations) {
System.out.println(annotation);
}
}
}
- 运行结果
Annotations on ParentClass:
@MyInheritedAnnotation(value=Inherited Annotation)
Annotations on ChildClass:
@MyInheritedAnnotation(value=Inherited Annotation)
从上述结果可以看出,尽管我们只在 ParentClass 上使用了 @MyInheritedAnnotation 注解,但 ChildClass 也自动继承了该注解。