Anonotation的含义:中文翻译为"注解",实际上表示的是一种注释的语法,这种注释和代码的注释是不一样的,代码的注释如单行注释使用双斜杠"//",或多行注释使用"/*...*/"等是给程序员看的,其主要是为了增加代码的可读性,便于代码的后期维护。而Annotation主要是服务于编译器,属于一种配置信息。在本质上,Annotation提供了一种与程序元素关联任何信息或任何元数据的方式。Annotation可以像修饰符一样被使用,它可以应用于任何程序元素(如包,类型,构造方法,方法。成员变量,参数,本地变量)的声明中。这些信息被存储在Annotation的"name=value"结构对中。Annotation类型实际上是一种接口,能够通过Java反射API(应用程序接口)的方式提供对其信息的访问。
1.基本的Annotation
@Override:用在方法上,用来告诉编译器这个方法是改写父类的。
@Deprecated:标识某个程序元素(类,方法等)已经过时,不建议使用。
@SuppressWarnings:关闭编译器对程序元素(类,方法,成员变量等)的警告。
2.JDK的元Annotation
元Annotation是用于修饰其它的Annotation定义
(1)@Retention:只能用于修改一个Annotation定义,用于指定被修饰的Annotation的保存范围。
@Retention包含一个RetentionPolicy枚举类型的value成员变量用于指定Annotation的保存范围。
RetentionPolicy的值只能是如下三个:
- RetentionPolicy.CLASS:编译器将把Annotation记录在类文件中,但不会被加载到JVM中。如果一个Annotation声明时没有指定范围,则默认值是CLASS。
- RetentionPolicy.SOURCE:此Annotation类型的信息只会记录在源文件中,编译时将被编译器丢弃,及此Annotation信息不会保存在编译好的类文件中。
- RetentionPolicy.RUNTIME:此Annotation类型的信息将会保留在源文件,类文件中,在执行时也会加载到Java虚拟机(JVM)中,因此使用该值的时候,程序可以利用反射获取该Annotation信息
基本的Annotation的保存范围分别是:
@Override采用的是@Retention(value = RetentionPolicy.SOURCE),注解信息只能在源文件中出现。
@Deprecated采用的是@Retention(value = RetentionPolicy.RUNTIME),注解信息在执行时出现。
@SuppressWarnings采用的是@Retention(value = RetentionPolicy.SOURCE),注解信息只能在源文件中出现。
(2)@Target只能修饰一个Annotation定义,用于指定修饰的Annotation能用于修饰那些程序元素。
@Target包含一个ElementType枚举类型的value成员变量用于指定Annotation的修饰位置。
- ElementType.ANNOTATION_TYPE:只能修饰Annotation
- ElementType.FIELD:只能修饰成员变量
- ElementType.LOCAL_VARIABLE:只能修饰局部变量
- ElementType.METHOD:只能修饰方法
- ElementType.PACKAGE:只能修饰包
- ElementType.PARAMETER:只能修饰参数
- ElementType.TYPE:只能修饰类,接口(包括注释类型)或枚举
(3)@Documented用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。
(4)@Inherited指定被它修饰的Annotation将具有继承性,如果某个类使用了自定义的Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动被该自定义的Annotation修饰。
3.自定义Annotation
自定义Annotation的语法如下:
public @interface Annotation的名称{
数据类型 变量名称();//定义的变量必须在后面加上()
}
示例
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
public @interface MyAnnotation {
String key();
String value() default "";//default用于设置该变量的默认值
boolean required() default true;
String[] strArr() default {};
}
使用@interface就相当于继承了Annotation接口。在程序中只要使用了@interface声明Annotation,那么此Annotation实际上就相当于继承了java.lang.annotation.Annotation接口。
根据Annotation是否包含成员变量,可以把Annotation分为如下两类:
- 标记Annotation:一个没有定义成员变量的Annotation类型被称为标记。这种Annotation仅利用自身的存在与否来为我们提供信息,例如@Override等Annotation。
- 元数据Annotation:包含成员变量的Annotation,因为它们可以接受更多的元数据,所以也被称为元数据Annotation。
4.提取Annotation信息
AnnotationElement接口是所有程序元素(如Class,Method,Constructor等)的父接口,所以程序通过反射获取某个类的AnnotationElement对象(如Class,Method,Constructor等)之后,程序就可以调用该对象的如下3个方法来访问Annotation信息。
getAnnotation(Class<T> annotationClass):返回该程序元素上存在的指定类型的注释,如果该类型的注释不存在,则返回null。
Annotation[] getAnnotations():返回该程序元素上存在的所有注释。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否存在指定类型的注释,如果存在则返回true,否则返回false。
注意:只有定义采用的是@Retention(value = RetentionPolicy.RUNTIME)的Annotation才能在程序运行时被反射机制取得。
示例
@MyAnnotation(key = "four", value = "3", required = false)
public class Info {
@MyAnnotation(key = "three", value = "zs")
private String name;
@MyAnnotation(key = "one", value = "1", required = false)
public Info() {
}
@Override
@Deprecated
@MyAnnotation(key = "two")
public String toString() {
return "hello";
}
}
public class GetAnnotation {
public static void main(String[] args) throws Exception {
Class<?> cla = Class.forName("com.example.demo.annotation.domain.Info");
System.out.println("获取类上的@MyAnnotation注释信息");
if (cla.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = null;
myAnnotation = cla.getAnnotation(MyAnnotation.class);
String key = myAnnotation.key();
String value = myAnnotation.value();
boolean required = myAnnotation.required();
System.out.println(key + "," + value + "," + required);
}
Annotation[] m0 = cla.getAnnotations();
for (int i = 0; i < m0.length; i++) {
System.out.println(m0[i]);
}
System.out.println("获取方法上的@MyAnnotation信息");
Method toStringMethod = cla.getMethod("toString");
System.out.println(toStringMethod);
if (toStringMethod.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = null;
myAnnotation = toStringMethod.getAnnotation(MyAnnotation.class);
String key = myAnnotation.key();
String value = myAnnotation.value();
boolean required = myAnnotation.required();
System.out.println(key + "," + value + "," + required);
}
Annotation[] m = toStringMethod.getAnnotations();
for (int i = 0; i < m.length; i++) {
System.out.println(m[i]);
}
System.out.println("获取构造器上的@MyAnnotation的信息");
Constructor myAnnotation1 = cla.getConstructor();
System.out.println(myAnnotation1);
if (myAnnotation1.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = (MyAnnotation) myAnnotation1.getAnnotation(MyAnnotation.class);
String key = myAnnotation.key();
String value = myAnnotation.value();
boolean required = myAnnotation.required();
System.out.println(key + "," + value + "," + required);
}
Annotation[] m1 = myAnnotation1.getAnnotations();
for (int i = 0; i < m1.length; i++) {
System.out.println(m1[i]);
}
System.out.println("获取属性上的@MyAnnotation的信息");
Field myAnnotation2 = cla.getDeclaredField("name");
System.out.println(myAnnotation2);
if (myAnnotation2.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = myAnnotation2.getAnnotation(MyAnnotation.class);
String key = myAnnotation.key();
String value = myAnnotation.value();
boolean required = myAnnotation.required();
System.out.println(key + "," + value + "," + required);
}
Annotation[] m2 = myAnnotation2.getAnnotations();
for (int i = 0; i < m2.length; i++) {
System.out.println(m2[i]);
}
}
}