一、基础知识
Java支持在源文件中嵌入补充信息,这类信息称为注解(元数据)。注解不会改变程序的行为,因此也不会改变程序的语义。
二、声明
上述声明表示注解。注解都只能包含方法声明,不能为这些方法提供实现,而是由Java实现。所有的注解类型都自动扩展了Annotation接口,其指定了annotationType方法,该方法返回调用注解的对象。
在声明了注解之后就可以用来注解声明了。所有的声明都可以有与之关联的注解,甚至注解本身也可以被注解。使用注解时,需要为注解的成员提供值。
class Solution { @interface MyAnnotation { String str(); int val() default 0;//使用默认值 } @MyAnnotation(str = "printHello", val = 1) public static void print() { System.out.println("Hello"); } }
三、指定保留策略
注解保留策略决定了在什么位置丢弃注解。Java于java.lang.annotation.RetentionPolicy枚举中定义了三种保留策咯:
- SOURCE策略仅在源文件中保留注解而编译期抛弃。
- CLASS策略在编译时将注解存储至class文件中但是Java虚拟机不能得到这些注解。
- RUNTIME策略提供了永久的注解。
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String str(); int val(); }
四、使用反射获取注解
如果为注解指定RUNTIME保留策略,任何程序在运行的时候都可以使用反射来查询注解。反射时能够在运行时获取类相关信息的特性。
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String str(); int val(); } class Solution { @MyAnnotation(str = "Annotation content", val = 0) public static void printAnnotation() { Solution ob = new Solution(); try { Class<?> c = ob.getClass(); Method method = c.getMethod("printAnnotation"); MyAnnotation anno = method.getAnnotation(MyAnnotation.class); System.out.println(anno); } catch (NoSuchMethodException exc) { System.out.println("Method does not exist"); } } public static void main(String[] args) { printAnnotation(); } }
获取所有注解。
import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotationA { String str(); } @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotationB { int val(); } @MyAnnotationA(str = "Class annotation content") @MyAnnotationB(val = 1) class Solution { @MyAnnotationA(str = "Method annotation content") @MyAnnotationB(val = 0) public static void printAnnotation() { Solution ob = new Solution(); try { Annotation[] annos = ob.getClass().getAnnotations(); for (Annotation a : annos) System.out.println(a); Method method = ob.getClass().getMethod("printAnnotation"); annos = method.getAnnotations(); for (Annotation a : annos) System.out.println(a); } catch (NoSuchMethodException exc) { System.out.println("Method does not exist"); } } public static void main(String[] args) { printAnnotation(); } }
五、相关接口
getAnnotation方法和getAnnotations方法均由AnnotatedElement接口定义,该接口支持注解反射。AnnotatedElement接口此外还定义了如下方法:
- getDeclaredAnnotations方法返回调用对象中存在的所有非继承注解。
- isAnnotationPresent方法返回注解与对象是否关联。
六、标记注解
标记注解是特殊类型的注解,其中不包含成员。标记注解的唯一目的就是标记声明,确定标记注解是否存在的最好方式是使用isAnnotationPresent方法。
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface Marker {} class Solution { @Marker public static void printAnnotation() { Solution ob = new Solution(); try { Class<?> c = ob.getClass(); Method method = c.getMethod("printAnnotation"); System.out.println(method.isAnnotationPresent(Marker.class)); } catch (NoSuchMethodException exc) { System.out.println("Method does not exist"); } } public static void main(String[] args) { printAnnotation(); } }
七、单成员注解
单成员注解通常只包含一个成员,允许使用缩写形式指定成员值。该成员名称必须是value,若包含其他成员则必须有默认值。
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface Single { String value(); int i() default 0; } class Solution { @Single("printHello") public static void print() { System.out.println("Hello"); } }
八、内置注解
- Retention指定保留策略,只能注解其他注解。
- Documented是标记接口,通知其他工具注解将被文档化,只能注解其他注解。
- Target指定可以应用注解的声明类型,只能注解其他注解。可同时指定多个值。
- Inherited是标记注解,指明超类注解可被子类继承,只能注解其他注解。
- Override是标记注解,指明必须重写超类中的方法,只能注解方法。
- Deprecated是标记注解,指明声明是过时的,已被新的形式取代。
- FunctionalInterface指明接口是函数式接口。函数式接口仅包含一个由lambda表达式所使用的抽象方法。
- SafeVarargs是标记注解,指明没有发生与可变长度参数相关的不安全动作,只能用于方法和构造函数。
- SuppressWarnings指明抑制编译器可能会报告的警告,使用字符串表示警告名称。
九、类型注解
最早的注解只能应用于声明,但是现在已经能应用于使用类型的大多数地方。扩展后的这种注解称为类型注解。类型注解允许工具对代码执行额外检查,从而帮助避免错误。
import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.CONSTRUCTOR) @interface ConstructorAnno {} @Target(ElementType.TYPE_USE) @interface TypeAnno {} @Target(ElementType.TYPE_PARAMETER) @interface ParameterAnno {} @Target(ElementType.FIELD) @interface FieldAnno {} class Solution<@ParameterAnno T> { @FieldAnno @TypeAnno private T data; @ConstructorAnno Solution(@TypeAnno T data) { this.data = data; } }