元注解 - @Documented
Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
以下代码在使用Javadoc工具可以生成@TestDocAnnotation
注解信息。
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Documented @Target({ElementType.TYPE,ElementType.METHOD}) public @interface TestDocAnnotation { public String value() default "default"; }
元注解 - @Inherited
Inherited注解的作用:被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。
我们来测试下这个注解:
- 定义@Inherited注解:
@Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) public @interface TestInheritedAnnotation { String [] values(); int number(); }
使用这个注解
@TestInheritedAnnotation(values = {"value"}, number = 10) public class Person { } class Student extends Person{ @Test public void test(){ Class clazz = Student.class; Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.toString()); } } }
输出
xxxxxxx.TestInheritedAnnotation(values=[value], number=10)
1
即使Student类没有显示地被注解@TestInheritedAnnotation,但是它的父类Person被注解,而且@TestInheritedAnnotation被@Inherited注解,因此Student类自动有了该注解。
元注解 - @Repeatable (Java8)
@Repeatable请参考Java 8 - 重复注解
元注解 - @Native (Java8)
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可
注解与反射接口
定义注解后,如何获取注解中的内容呢?反射包java.lang.reflect下的AnnotatedElement接口提供这些方法。这里注意:只有注解被定义为RUNTIME后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。我们看下具体的先关接口
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。注意:此方法会忽略注解对应的注解容器。
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
Annotation[] getAnnotations()
返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组。
<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
返回该程序元素上存在的、指定类型的注解数组。没有注解对应类型的注解时,返回长度为0的数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。getAnnotationsByType方法与 getAnnotation的区别在于,getAnnotationsByType会检测注解对应的重复注解容器。若程序元素为类,当前类上找不到注解,且该注解为可继承的,则会去父类上检测对应的注解。
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。如果没有注释直接存在于此元素上,则返回null
<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解及注解对应的重复注解容器。与此接口中的其他方法不同,该方法将忽略继承的注解。如果没有注释直接存在于此元素上,则返回长度为零的一个数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。
自定义注解
当我们理解了内置注解, 元注解和获取注解的反射接口后,我们便可以开始自定义注解了。这个例子我把上述的知识点全部融入进来, 代码很简单:
定义自己的注解
package com.pdai.java.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyMethodAnnotation { public String title() default ""; public String description() default ""; }
使用注解
package com.pdai.java.annotation; import java.io.FileNotFoundException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; public class TestMethodAnnotation { @Override @MyMethodAnnotation(title = "toStringMethod", description = "override toString method") public String toString() { return "Override toString method"; } @Deprecated @MyMethodAnnotation(title = "old static method", description = "deprecated old static method") public static void oldMethod() { System.out.println("old method, don't use it."); } @SuppressWarnings({"unchecked", "deprecation"}) @MyMethodAnnotation(title = "test method", description = "suppress warning static method") public static void genericsTest() throws FileNotFoundException { List l = new ArrayList(); l.add("abc"); oldMethod(); } }
用反射接口获取注解信息
在TestMethodAnnotation中添加Main方法进行测试:
public static void main(String[] args) { try { // 获取所有methods Method[] methods = TestMethodAnnotation.class.getClassLoader() .loadClass(("com.pdai.java.annotation.TestMethodAnnotation")) .getMethods(); // 遍历 for (Method method : methods) { // 方法上是否有MyMethodAnnotation注解 if (method.isAnnotationPresent(MyMethodAnnotation.class)) { try { // 获取并遍历方法上的所有注解 for (Annotation anno : method.getDeclaredAnnotations()) { System.out.println("Annotation in Method '" + method + "' : " + anno); } // 获取MyMethodAnnotation对象信息 MyMethodAnnotation methodAnno = method .getAnnotation(MyMethodAnnotation.class); System.out.println(methodAnno.title()); } catch (Throwable ex) { ex.printStackTrace(); } } } } catch (SecurityException | ClassNotFoundException e) { e.printStackTrace(); } }
测试的输出
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @java.lang.Deprecated() Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @com.pdai.java.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method) old static method Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.genericsTest() throws java.io.FileNotFoundException' : @com.pdai.java.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method) test method Annotation in Method 'public java.lang.String com.pdai.java.annotation.TestMethodAnnotation.toString()' : @com.pdai.java.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method) toStringMethod