java中的Annotation功能可用于类、构造方法、普通方法、成员变量(全局变量)、参数等声明中。该功能不影响程序的正常运行。
定义Annotation
基本语法:
定义Annotation的关键字是@interface。
@Target(ElementType.METHOD)//表示该注解只能用于方法
//................还可以有其它设置
public @interface Lock {
/**
* 遵循接口(interface)规范,使用默认的权限修饰符,不要显示声明public等
* String 称为成员类型
* 注解中的成员类型只能是基本数据类型、String、Class、注解(annotation)、枚举
* 或者它们的一维数组类型。比如Integer是不支持的。
*
* value 称为成员名称,命名符合java命名要求即可。
* 假如在自定义注解中只有一个成员,通常都命名为value。
*
* 这里应该理解为方法,不能理解为属性,所以成员名称后面是带括号()的。
* */
String value();
/**
* 在声明自定义注解成员的时候可以给定一个默认值
* */
int expire() default 5;
Class<String> type() default String.class;
}
@Target设置(该注解被设置为只能用于Annotation)
@Target设置用来设置Annotation类型适用的程序元素种类(即限制注解使用的范围,如果未设置@Target,则表示不限制使用范围)。
@Target注解中定了一个成员ElementType的一维数组类型,用来设置@Target,是一个枚举类。所有属性如下:
枚举常量 | 说明 |
ANNOTATION_TYPE | 表示只能用于注解 |
TYPE | 表示用于类、接口和枚举,以及Annotation类型 |
CONSTRUCTOR | 表示用于构造方法 |
FIELD | 表示用于成员变量和枚举常量 |
METHOD | 表示用于方法 |
PARAMETER | 表示用于参数 |
LOCAL_VARIABLE | 表示用于局部变量 |
PACKAGE | 表示用于包 |
TYPE_PARAMETER | jdk1.8以后提供,用于泛型 |
TYPE_USE | jdk1.8以后提供,用于任意类类型 |
基本用法:
@Target(value= {ElementType.METHOD, ElementType.FIELD})//@Target注解中定义的成员ElementType是枚举的一维数组类型
@Target(ElementType.METHOD)//如果只需要单个,缩写指定单个属性即可
public @interface Lock {
//..................成员
}
@Retention设置(该注解被设置为只能用于Annotation)
@Retention设置Aannotation的有效范围。成员RetentionPolicy是一个枚举类型,用来设置@Retention。所有属性如下:
枚举常量 | 说明 |
SOURCE | 表示不编译Annotaion到类文件中,有效范围最小 |
CLASS | 表示编译Annotation到类文件中,但在运行时不加载Annotation到JVM中 |
RUNTIME | 表示在运行时加载Annotation到JVM中,有效范围最大 |
基本用法:
@Retention(RetentionPolicy.RUNTIME)
public @interface Lock {
//..................成员
}
@Documented设置
Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
基本用法:
@Documented
public @interface Lock {
//.......成员变量
}
@Inherited设置
表示该注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。对于类来说,子类要继承父类的注解需要该注解被 @Inherited
标记。
基本用法:
@Inherited
public @interface Lock {
//..........成员
}
简单示例
1、定义一个用来注解构造方法的Annotation,只有一个类型为String的成员,有效范围在运行时加载到jvm中。
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constructor_Annotation {
String value() default "默认的构造方法";
}
2、定义一个用来注解成员属性、方法和参数的Annotation,有效范围在运行时加载到jvm中。
@Target(value= {ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Fiel_Method_Parameter_Annotation {
String describle();
Class type() default void.class;
}
3、编写一个类Record,在该类中运用前面定义的Annotation对构造方法、字段、方法和参数进行注解。
public class Record {
@Fiel_Method_Parameter_Annotation(describle="编号", type=int.class)
private int id;
@Fiel_Method_Parameter_Annotation(describle="姓名", type=String.class)
private String name;
@Constructor_Annotation//该注解成员value给了默认值"默认的构造方法" //注解构造方法
public Record() {
super();
}
@Constructor_Annotation(value="有参构造方法")
public Record(@Fiel_Method_Parameter_Annotation(describle="编号", type=int.class)int id, //注解参数
@Fiel_Method_Parameter_Annotation(describle="姓名", type=String.class)String name) {
super();
this.id = id;
this.name = name;
}
@Fiel_Method_Parameter_Annotation(describle="获取编号", type=int.class)//注解方法
public int getId() {
return id;
}
@Fiel_Method_Parameter_Annotation(describle="设置编号")
public void setId(@Fiel_Method_Parameter_Annotation(describle="编号", type=int.class)int id) {//注解参数
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
访问Annotation信息(通过反射读取)
如果再定义Annotation时,将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时通过反射就可以获取到相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。
类Constructor<T>、Field、Method均继承自AccessibleObject,在AccessibleObject类中定义了3个关于Annotation的方法。
方法 | 说明 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 用来查看是否添加了指定类型的Annotation,有返回true,否则返回false |
getAnnotation(Class<T> annotationClass) | 用来获取指定类型的Annotation,如果不存在返回null |
Annotation[] getAnnotations() | 获取目标多有的Annotation,返回的是一个数组 |
在Constructor<T>、和Method中还单独定义了Annotation[][] getParameterAnnotations()方法,用来获取为多有参数添加的Annotation,将以Annotation的二维数组返回,在数组中的顺序与声明的顺序相同,如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。
简单的示例
沿用上面的Record类。
public class Test {
public static void main(String[] args) {
Record record = new Record();
Class<? extends Record> recordClass = record.getClass();
//获取目标对象的所有属性
Field[] fields = recordClass.getDeclaredFields();
for(int i=0;i<fields.length;i++) {
Field field = fields[i];
if(field.isAnnotationPresent(Fiel_Method_Parameter_Annotation.class)) {
Fiel_Method_Parameter_Annotation fiel_Method_Parameter_Annotation = field.getAnnotation(Fiel_Method_Parameter_Annotation.class);
System.out.println("成员属性描述 :" + fiel_Method_Parameter_Annotation.describle());
System.out.println("成员属性类型 :" + fiel_Method_Parameter_Annotation.type());
}
}
//获取目标对象的所有构造方法
Constructor<?>[] constructors = recordClass.getDeclaredConstructors();
for(int i=0; i<constructors.length; i++) {
Constructor<?> constructor = constructors[i];
if(constructor.isAnnotationPresent(Constructor_Annotation.class)) {
Constructor_Annotation constructor_Annotation = constructor.getAnnotation(Constructor_Annotation.class);
System.out.println(constructor_Annotation.value());//打印目标类上的@Constructor_Annotation注解的value()成员
}
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
for(int j=0; j<parameterAnnotations.length; j++) {
int length = parameterAnnotations[j].length;//获取指定参数注解的长度
if(length == 0) {//表示没有添加Annotation的参数
System.out.println("没有添加Annotation的参数");
}else {
for(int k=0; k<length; k++) {
Fiel_Method_Parameter_Annotation fiel_Method_Parameter_Annotation = (Fiel_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.println("参数描述 : " + fiel_Method_Parameter_Annotation.describle());
System.out.println("参数类型 : " + fiel_Method_Parameter_Annotation.type());
}
}
}
}
//获取目标对象的方法
Method[] methods = recordClass.getDeclaredMethods();
for(int i=0;i<methods.length;i++) {
Method method = methods[i];
if(method.isAnnotationPresent(Fiel_Method_Parameter_Annotation.class)) {
Fiel_Method_Parameter_Annotation fiel_Method_Parameter_Annotation = method.getAnnotation(Fiel_Method_Parameter_Annotation.class);
System.out.println("方法描述 :" + fiel_Method_Parameter_Annotation.describle());
System.out.println("方法返回类型 :" + fiel_Method_Parameter_Annotation.type());
}
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for(int j=0;j<parameterAnnotations.length;j++) {
int length = parameterAnnotations[j].length;
if(length == 0) {//表示没有添加Annotation的参数
System.out.println("没有添加Annotation的参数");
}else {
for(int k=0;k<length;k++) {
Fiel_Method_Parameter_Annotation fiel_Method_Parameter_Annotation = (Fiel_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.println("参数描述 : " + fiel_Method_Parameter_Annotation.describle());
System.out.println("参数类型 : " + fiel_Method_Parameter_Annotation.type());
}
}
}
}
}
}
结果:
成员属性描述 :编号
成员属性类型 :int
成员属性描述 :姓名
成员属性类型 :class java.lang.String
默认的构造方法
有参构造方法
参数描述 : 编号
参数类型 : int
参数描述 : 姓名
参数类型 : class java.lang.String
方法描述 :获取编号
方法返回类型 :int
没有添加Annotation的参数
方法描述 :设置编号
方法返回类型 :void
参数描述 : 编号
参数类型 : int
总结
利用Annotation功能,可以对类、构造方法、属性(成员变量)、方法、参数等进行注解,在程序运行时通过反射可以获取被Annotation注解的信息,有了这些信息,就可以搞事情了。