6、Java内置注解与其它元注解
接着看看Java提供的内置注解,主要有3个,如下:
@Override:用于标明此方法覆盖了父类的方法,源码如下
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Deprecated:用于标明已经过时的方法或类,源码如下,关于@Documented稍后分析:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
@SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告,其实现源码如下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
其内部有一个String数组,主要接收值如下:
deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型
(Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告; serial:当在可序列化的类上缺少
serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。
这个三个注解比较简单,看个简单案例即可:
//注明该类已过时,不建议使用 @Deprecated class A{ public void A(){ } //注明该方法已过时,不建议使用 @Deprecated() public void B(){ } } class B extends A{ @Override //标明覆盖父类A的A方法 public void A() { super.A(); } //去掉检测警告 @SuppressWarnings({"uncheck","deprecation"}) public void C(){ } //去掉检测警告 @SuppressWarnings("uncheck") public void D(){ } }
前面我们分析了两种元注解,@Target和@Retention,除了这两种元注解,Java还提供了另外两种元注解,@Documented和@Inherited,下面分别介绍:
@Documented 被修饰的注解会生成到javadoc中
@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentA { } //没有使用@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentB { } //使用注解 @DocumentA @DocumentB public class DocumentDemo { public void A(){ } }
可以发现使用@Documented元注解定义的注解(@DocumentA)将会生成到javadoc中,而@DocumentB则没有在doc文档中出现,这就是元注解@Documented的作用。
@Inherited 可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解,如下:
@Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentA { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentB { } @DocumentA class A{ } class B extends A{ } @DocumentB class C{ } class D extends C{ } //测试 public class DocumentDemo { public static void main(String... args){ A instanceA=new B(); System.out.println("已使用的@Inherited注解:"+Arrays.toString(instanceA.getClass().getAnnotations())); C instanceC = new D(); System.out.println("没有使用的@Inherited注解:"+Arrays.toString(instanceC.getClass().getAnnotations())); } /** * 运行结果: 已使用的@Inherited注解:[@com.zejian.annotationdemo.DocumentA()] 没有使用的@Inherited注解:[] */ }
三、注解与反射机制
前面经过反编译后,我们知道Java所有注解都继承了Annotation接口,也就是说 Java使用Annotation接口代表注解元素,该接口是所有Annotation类型的父接口。同时为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口,它简要含义如下:
Class:类的Class对象定义
Constructor:代表类的构造器定义
Field:代表类的成员变量定义
Method:代表类的方法定义
Package:代表类的包定义
下面是AnnotatedElement中相关的API方法,以上5个类都实现以下的方法
简单案例演示如下:
import java.lang.annotation.Annotation; import java.util.Arrays; /** * Created by zejian on 2017/5/20. * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] */ @DocumentA class A{ } //继承了A类 @DocumentB public class DocumentDemo extends A{ public static void main(String... args){ Class<?> clazz = DocumentDemo.class; //根据指定注解类型获取该注解 DocumentA documentA=clazz.getAnnotation(DocumentA.class); System.out.println("A:"+documentA); //获取该元素上的所有注解,包含从父类继承 Annotation[] an= clazz.getAnnotations(); System.out.println("an:"+ Arrays.toString(an)); //获取该元素上的所有注解,但不包含继承! Annotation[] an2=clazz.getDeclaredAnnotations(); System.out.println("an2:"+ Arrays.toString(an2)); //判断注解DocumentA是否在该元素上 boolean b=clazz.isAnnotationPresent(DocumentA.class); System.out.println("b:"+b); /** * 执行结果: A:@com.zejian.annotationdemo.DocumentA() an:[@com.zejian.annotationdemo.DocumentA(), @com.zejian.annotationdemo.DocumentB()] an2:@com.zejian.annotationdemo.DocumentB() b:true */ } }