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