Java注解用法详解(附带实例)
Java 中提供了 Annotation 注解功能,该功能可用于类、构造方法、成员变量、成员方法、参数等的声明中。该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。
例如,下面的代码定义一个 Annotation 类型:
上面定义的 Annotation 类型 @NoMemberAnnotation 未包含任何成员,这样的 Annotation 类型被称为 marker annotation。
下面的代码定义一个只包含一个成员的 Annotation 类型:
下面的代码定义一个包含多个成员的 Annotation 类型:
在为 Annotation 类型定义成员时,也可以为成员设置默认值。例如,下面的代码在定义 Annotation 类型时为成员设置默认值:
在定义 Annotation 类型时,还可以通过 Annotation 类型 @Target 来设置 Annotation 类型适用的程序元素种类。如果未设置 @Target,则表示适用于所有程序元素。
枚举类 ElementType 中的枚举常量用来设置 @Target,如下表所示。
通过 Annotation 类型 @Retention 可以设置 Annotation 的有效范围。枚举类 RetentionPolicy 中的枚举常量用来设置 @Retention,如下表所示。
如果未设置 @Retention,那么 Annotation 的有效范围为枚举常量 CLASS 表示的范围。
【实例 1】创建自定义的注解。首先定义一个用来注释构造方法的 Annotation 类型 @Constructor_Annotation,有效范围为在运行时加载 Annotation 到 JVM 中。完整代码如下:
然后定义一个用来注释字段、方法和参数的 Annotation 类型 @Field_Method_Parameter_Annotation,有效范围为在运行时加载 Annotation 到 JVM 中。完整代码如下:
最后编写一个 Record 类,在该类中运用前面定义的 Annotation 类型 @Constructor_Annotation 和 @Field_Method_Parameter_Annotation 来对构造方法、字段、方法和参数进行注释。完整代码如下:
Constructor 类、Field 类和 Method 类均继承了 AccessibleObject 类,在AccessibleObject 中定义了 3 个关于 Annotation 的方法:
在 Constructor 类和 Method 类中还定义了方法 getParameterAnnotations(),用来获得为所有参数添加的 Annotation,将以 Annotation 类型的二维数组返回,在数组中的顺序与声明的顺序相同。如果没有参数,则返回一个长度为 0 的数组;如果存在未添加 Annotation 的参数,则将用一个长度为 0 的嵌套数组占位。
【实例 2】访问注释中的信息。本例将对实例 1 进行扩展,实现在程序运行时通过反射访问 Record 类中的 Annotation 信息。首先编写访问构造方法及其包含参数的 Annotation 信息的代码。关键代码如下:
然后编写访问字段的 Annotation 信息的代码。关键代码如下:
最后编写访问方法及其包含参数的 Annotation 信息的代码。关键代码如下:
Java定义注解类型
在定义 Annotation 类型时,也需要用到用来定义接口的 interface 关键字,但需要在 interface 关键字前加一个“@”符号,即定义 Annotation 类型的关键字 为@interface,这个关键字的隐含意思是继承了 java.lang.annotation.Annotation 接口。例如,下面的代码定义一个 Annotation 类型:
public @interface NoMemberAnnotation {
}
上面定义的 Annotation 类型 @NoMemberAnnotation 未包含任何成员,这样的 Annotation 类型被称为 marker annotation。
下面的代码定义一个只包含一个成员的 Annotation 类型:
public @interface OneMemberAnnotation {
String value();
}
- String:成员类型。可用的成员类型有 String、Class、primitive、enumerated 和 annotation,以及所列类型的数组。
- value:成员名称。如果在所定义的 Annotation 类型中只包含一个成员,通常将成员名称命名为 value。
下面的代码定义一个包含多个成员的 Annotation 类型:
public @interface MoreMemberAnnotation {
String describe();
Class type();
}
在为 Annotation 类型定义成员时,也可以为成员设置默认值。例如,下面的代码在定义 Annotation 类型时为成员设置默认值:
public @interface DefaultValueAnnotation {
String describe() default "默认值";
Class type() default void.class;
}
在定义 Annotation 类型时,还可以通过 Annotation 类型 @Target 来设置 Annotation 类型适用的程序元素种类。如果未设置 @Target,则表示适用于所有程序元素。
枚举类 ElementType 中的枚举常量用来设置 @Target,如下表所示。
| 枚举常量 | 说明 |
|---|---|
| ANNOTATION_TYPE | 表示用于Annotation类型 |
| TYPE | 表示用于类、接口和枚举,以及Annotation类型 |
| CONSTRUCTOR | 表示用于构造方法 |
| FIELD | 表示用于成员变量和枚举常量 |
| METHOD | 表示用于方法 |
| PARAMETER | 表示用于参数 |
| LOCAL_VARIABLE | 表示用于局部变量 |
| PACKAGE | 表示用于包 |
通过 Annotation 类型 @Retention 可以设置 Annotation 的有效范围。枚举类 RetentionPolicy 中的枚举常量用来设置 @Retention,如下表所示。
| 枚举常量 | 说明 |
|---|---|
| SOURCE | 表示不编译Annotation到类文件中,有效范围最小 |
| CLASS | 表示编译Annotation到类文件中,但是在运行时不加载Annotation到JVM中 |
| RUNTIME | 表示在运行时加载Annotation到JVM中,有效范围最大 |
如果未设置 @Retention,那么 Annotation 的有效范围为枚举常量 CLASS 表示的范围。
【实例 1】创建自定义的注解。首先定义一个用来注释构造方法的 Annotation 类型 @Constructor_Annotation,有效范围为在运行时加载 Annotation 到 JVM 中。完整代码如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.CONSTRUCTOR) //用于构造方法
@Retention(RetentionPolicy.RUNTIME) //在运行时加载Annotation到JVM中
public @interface Constructor_Annotation {
String value() default "默认构造方法";
}
然后定义一个用来注释字段、方法和参数的 Annotation 类型 @Field_Method_Parameter_Annotation,有效范围为在运行时加载 Annotation 到 JVM 中。完整代码如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//用于字段、方法和参数
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME) //在运行时加载Annotation到JVM中
public @interface Field_Method_Parameter_Annotation {
String describe(); //定义一个没有默认值的String型成员
Class<?> type() default void.class; //定义一个具有默认值的Class型成员
}
最后编写一个 Record 类,在该类中运用前面定义的 Annotation 类型 @Constructor_Annotation 和 @Field_Method_Parameter_Annotation 来对构造方法、字段、方法和参数进行注释。完整代码如下:
public class Record {
@Field_Method_Parameter_Annotation(describe = "编号", type = int.class)
int id;
@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class)
String name;
@Constructor_Annotation() //采用默认值注释构造方法
public Record() {
}
@Constructor_Annotation("立即初始化构造方法")
//注释构造方法
public Record(@Field_Method_Parameter_Annotation(describe = "编号", type = int.class) int id,
@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class) String name) {
this.id = id;
this.name = name;
}
@Field_Method_Parameter_Annotation(describe = "获得编号", type = int.class)
public int getId() {
return id;
}
@Field_Method_Parameter_Annotation(describe = "设置编号")
public void setId(
@Field_Method_Parameter_Annotation(describe = "编号", type = int.class) int id) {
this.id = id;
}
@Field_Method_Parameter_Annotation(describe = "获得姓名", type = String.class)
public String getName() {
return name;
}
@Field_Method_Parameter_Annotation(describe = "设置姓名")
public void setName(@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class) String name) {
this.name = name;
}
}
Java访问注解信息
如果在定义 Annotation 类型时将 @Retention 设置为 RetentionPolicy.RUNTIME,那么在运行程序时通过反射就可以获取到相关的 Annotation 信息,如获取构造方法、字段和方法的 Annotation 信息。Constructor 类、Field 类和 Method 类均继承了 AccessibleObject 类,在AccessibleObject 中定义了 3 个关于 Annotation 的方法:
- isAnnotationPresent(Class<? extends Annotation> annotationClass)用来查看是否添加了指定类型的 Annotation,如果是,则返回 true,否则返回 false;
- getAnnotation(Class<T> annotationClass) 用来获得指定类型的 Annotation,如果存在,则返回相应的对象,否则返回 null;
- getAnnotations() 用来获得所有的 Annotation,该方法将返回一个 Annotation 数组。
在 Constructor 类和 Method 类中还定义了方法 getParameterAnnotations(),用来获得为所有参数添加的 Annotation,将以 Annotation 类型的二维数组返回,在数组中的顺序与声明的顺序相同。如果没有参数,则返回一个长度为 0 的数组;如果存在未添加 Annotation 的参数,则将用一个长度为 0 的嵌套数组占位。
【实例 2】访问注释中的信息。本例将对实例 1 进行扩展,实现在程序运行时通过反射访问 Record 类中的 Annotation 信息。首先编写访问构造方法及其包含参数的 Annotation 信息的代码。关键代码如下:
Class recordC = null;
try {
recordC = Class.forName("Record");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("------ 构造方法的描述如下 ------");
Constructor[] declaredConstructors = recordC.getDeclaredConstructors();
for (int i = 0; i < declaredConstructors.length; i++) { // 遍历构造方法
Constructor constructor = declaredConstructors[i];
if (constructor.isAnnotationPresent(Constructor_Annotation.class)) {
// 获得指定类型的注解
Constructor_Annotation ca = (Constructor_Annotation) constructor.getAnnotation(Constructor_Annotation.class);
// 获得注释信息
System.out.println(ca.value());
}
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); // 获得参数的注解
for (int j = 0; j < parameterAnnotations.length; j++) {
int length = parameterAnnotations[j].length;
if (length == 0)
System.out.println(" 未添加Annotation的参数");
else
for (int k = 0; k < length; k++) {
// 获得参数的注解
Field_Method_Parameter_Annotation pa = (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.println(" " + pa.describe()); // 获得参数描述
System.out.println(" " + pa.type());
}
}
System.out.println();
}
然后编写访问字段的 Annotation 信息的代码。关键代码如下:
System.out.println("------ 字段的描述如下 ------");
Field[] declaredFields = recordC.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
if (field.isAnnotationPresent(Field_Method_Parameter_Annotation.class)) {
// 获得指定类型的注解
Field_Method_Parameter_Annotation fa = field.getAnnotation(Field_Method_Parameter_Annotation.class);
System.out.println(" " + fa.describe()); // 获得字段的描述
System.out.println(" " + fa.type());
}
}
最后编写访问方法及其包含参数的 Annotation 信息的代码。关键代码如下:
System.out.println("------ 方法的描述如下 ------");
Method[] methods = recordC.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.isAnnotationPresent(Field_Method_Parameter_Annotation.class)) {
// 获得指定类型的注解
Field_Method_Parameter_Annotation ma = method.getAnnotation(Field_Method_Parameter_Annotation.class);
System.out.println(ma.describe()); // 获得方法的描述
System.out.println(ma.type()); // 获得方法的返回值类型
}
Annotation[][] parameterAnnotations = method.getParameterAnnotations(); // 获得参数的注解
for (int j = 0; j < parameterAnnotations.length; j++) {
int length = parameterAnnotations[j].length;
if (length == 0)
System.out.println(" 未添加Annotation的参数");
else
for (int k = 0; k < length; k++) {
// 获得指定类型的解
Field_Method_Parameter_Annotation pa = (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.println(" " + pa.describe()); // 获得参数的描述
System.out.println(" " + pa.type()); // 获得参数的类型
}
}
System.out.println();
}
运行结果如下:
------ 构造方法的描述如下 ------
默认构造方法
立即初始化构造方法
编号 int
姓名 class java.lang.String
-------- 字段的描述如下 --------
编号 int
姓名 class java.lang.String
-------- 方法的描述如下 --------
获得姓名
class java.lang.String
设置姓名
void
姓名 class java.lang.String
获得编号
int
设置编号
void
编号 int
ICP备案:
公安联网备案: