Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据
Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。 当然它也支持自定义Java标注.
元注解
Java中共有四种元注解:
//表示带有该注解的元素能被文档化
@Documented
//表示可被该注解修饰的元素种类
@Target
//表示该注解的作用域
@Retention
//表示该注解被自动继承
@Inherited
Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}```
@Documented表示被该注解修饰的元素能被Javadoc等工具文档化,但是注意到被@Target注解修饰且Target只能是Annotation类型,说明该注解只能用来修饰注解。作用域可至运行时。
@interface 定义注解的关键字。
#### Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}```
注意到Target和Documented注解有所不同,Target注解有方法体,返回值是一个ElementType类型的数组,ElementType是一个枚举类型,有TYPE(类、接口、枚举),METHOD(方法),FIELD(字段),PARAMETER(参数),CONSTRUCTOR(构造函数),LOCAL_VARIABLE(局部变量),ANNOTATION_TYPE(注解类型),PACKAGE(包),TYPE_PARAMETER(类型参数,1.8),TYPE_USE(类型使用,1.8),分别代表可被修饰的元素类型 。Target可传入一个或多个属性。
@Target(ElementType.ANNOTATION_TYPE)表示Target注解只能用来修饰注解,同时作用域为运行时。
@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}```
方法体中RetentionPolicy同样是一个枚举类型,SOURCE(保留在源文件,编译时被丢弃),CLASS(保留在源文件、编译后的class字节码,在运行时被丢弃),RUNTIME(一直保留直到运行时)。保留在运行时的可以通过反射去获得。
Retention也只能用来修饰注解,且作用域到运行时。
#### Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}```
Inherited表示注解是被自动继承的,即如果要在当前类查找这个元注解类型,但当前类并没有声明,将自动去其父类去寻找,直到找到或者找到顶层父类为止。注意这个被这个元注解标注的注解作用在类级别上,子类可以继承父类的注解,反之不可以。
Java中常用注解
@Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}```
告诉编辑器要复写超类的方法。@Override只能用于修饰方法,且作用域只停留在源码级别,即编译时会被丢弃,起标记作用。
#### @SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}```
用于告诉编辑器忽略警告信息。可用于修饰类、接口、字段、方法、参数、构造函数、局部变量。作用域停留在源码级别。
@SuppressWarnings使用时需要一个String类型的数组,如uncheck(忽略泛型未转换的警告),deprecation(忽略使用了过时的方法或字段警告)等。
@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}```
用来告诉编辑器,此方法、字段等不赞成使用。作用域为运行时。
# 反射获得运行时注解
注解由@Retention(RetentionPolicy.RUNTIME)修饰的为运行时注解,我们可以通过运行时反射获得注解信息。
比如我们自定义一个注解:
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String value() default "Boss";
}```
定义注解要用@interface关键字,target为方法,作用域为运行时,需要传入一个String类型的参数,注意到default “Boss”,即如果没有传参数将返回“Boss”,这个注解的意义为可以在方法体上使用该注解用于告知该函数的作者。
我们可以这样用:
@Author("Jack")
public void autorTest(){
System.out.print("I am author");
}```
如何通过反射在运行时获得Author信息呢?
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
String className = "AnnotationTest";
Method[] methods = Main.class.getClassLoader().loadClass(className).getMethods();
for (Method method : methods) {
//判断该元素是否存在指定注解
if (method.isAnnotationPresent(Author.class)) {
//获得该元素指定注解
Author author = method.getAnnotation(Author.class);
System.out.println("Author is " + author.value());
}
}
}
}```
首先通过反射获得制定类中的所有方法,遍历所有方法,找出其中含有制定注解的方法,在通过getAnnotation方法获得指定注解,进而可以获得注解信息。
需要用到的几个方法:
//判断是否被指定注解标注
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
//返回该元素所有注解
public Annotation[] getAnnotations()
//返回该元素指定注解
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)```
这样我们就完成了通过反射获得运行时注解。