16. Java Annotation(注释)
2014-09-04 13:57
656 查看
本章目标:
了解Annotation的作用掌握JDK1.5种内建的3种Annotation的使用
掌握自定义Annotation的语法及其应用
掌握@Retention、@Target、@Documented、@Inherited注释
Annotation的概念和作用、@Override注释的功能和用法、@Deprecated注释的功能和用法、@SuppressWarnings注释的功能和作用、自定义注释、提取注释信息、@Retention注释的功能和用法、@Target注释的功能和用法、@Documented注释的功能和用法、@Inherited注释的功能和用法、使用APT工具
16.1 Annotation简介
从JDK1.5开始,JAVA增加了对元数据(Metadata)的支持,也就是Annotation(注释),这种Annotation与其他的注释有一定的区别,也有一定的联系。本章所讲的Annotation,其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation注释,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。
Annotation提供了一种为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。
Annotation不影响程序运行,无论是否使用Annotation代码都可以正常运行。如果希望让程序中的Annotation在运行时起一定的作用,只能通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称为APT(Annotation Processing Tool)。
*****Annotation是一个接口(java.lang.annotation.Annotation),程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据*****
java.lang.annotation.Annotation接口的定义:
public interface Annotation{ public Class<? extends Annotation> annotationType(); //返回此annotation的注释类型 public boolean equals(Object obj); public int hashCode(); String toString(); }
16.2 系统内建的Annotation
Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。我们先看一下Java提供的4个基本Annotation的用法 --- 使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素。@Override: 覆写的Annotation
@Deprecated: 不赞成使用的Annotation
@SuppressWarnings: 压制安全警告的Annotation
@SafeVarargs: 压制安全警告
以上的Annotation都是java.lang.annotation.Annotation接口的子类。在Java中都有各自的定义:
Annotation | Java中的声明 |
@Override | @Target(value=Method) @Retention(value=SOURCE) public @interface Override |
@Deprecated | @Documented @Retention(value=RUNTIME) public @interface Deprecated |
@SuppressWarnings | @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE @Retention(value=SOURCE) public @interface SuppressWarnings |
16.2.1 @Override 限定重写父类方法
@Override就是用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法。主要在方法覆写时使用,用于保证方法覆写的正确性。范例:观察@Override注释的作用
package org.forfan06.annotationdemo; class Person{ public String getInfo(){ return "这是一个Person类"; } } class Student extends Person{ @Override public String getInfo(){ return "这是一个Student类"; } } public class OverrideAnnotationDemo01{ public static void main(String args[]){ Person per = new Student(); System.out.print(per.getInfo()); } }
运行结果:
这是一个Student类
以上程序中的子类Student继承Person类,之后覆写了Person类中的getInfo()方法,程序运行结果和之前的一样,唯一的不同只是在覆写的getInfo()方法前加上了@Override注释。这样做的目的是防止用于在覆写方法是将方法定义出错。
下面演示一个方法覆写错误的程序,观察编译信息
范例:错误的覆写
package org.forfan06.annotationdemo; class Person{ public String getInfo(){ return "这是一个Person类"; } } class Student extends Person{ @Override public String getinfo(){ return "这是一个Student类"; } } public class OverrideAnnotationDemo02{ public static void main(String args[]){ Person per = new Student(); System.out.print(per.getInfo()); } }
编译时出错:会在出错的地方给出指示,因为getInfo()和getinfo()是两个方法,所以使用了@Override注释,可以确保方法被正确覆写
/judge/data/20140904/1409815346955_java_34546/OverrideAnnotationDemo02.java:8: error: method does not override or implement a method from a supertype @Override ^ 1 error 编译错误================================================================================
@Override的使用限制:@Override在使用时只能在方法上应用;而其他元素,如类、属性等上市不能使用此Annotation的。
================================================================================
16.2.2 @Deprecated 标示已过时
@Deprecated注释用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。@Deprecated注释的主要功能是用来声明一个不建议使用的方法。如果在程序中使用了此方法,则在编译时将会出现警告信息。
范例:使用@Deprecated声明一个不建议使用的方法
package org.forfan06.annotationdemo; class Demo{ @Deprecated public String getInfo(){ return "被测试类"; } } public class DeprecatedAnnotationDemo01{ public static void main(String args[]){ Demo d = new Demo(); System.out.println(d.getInfo()); } }
以上的Demo类中的getInfo()方法上使用了@Deprecated注释声明,表示此方法不建议用户继续使用,所以在编译时将会出现一下的警告信息:
Note: /judge/data/20140904/1409815853025_java_34546/DeprecatedAnnotationDemo01.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details.虽然出现了警告信息,但是程序还是可以正常执行。因为@Deprecated注释只是表示该方法不建议使用,但并不是不能使用!!!
-->@Deprecated注释除了可以声明方法之外,还可以声明一个类<--
范例:在类声明中使用@Deprecated注释
package org.forfan06.annotationdemo; @Deprecated class Demo{ public String getInfo(){ return "测试测试"; } } public class DeprecatedAnnotationDemo02{ public static void main(String args[]){ Demo d = new Demo(); System.out.println(d.getInfo()); } }
编译时出现警告:
Note: /judge/data/20140904/1409816274992_java_34546/DeprecatedAnnotationDemo02.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details.
*****Thread类中的@Deprecated声明,在Thread类中有3个方法是使用了@Deprecated注释声明的: suspend()、stop()、resume()*****
*****@Deprecated的作用与文档注释中的@deprecated标记的作用基本相同,但它们的用法不同,前者是JDK1.5才支持的注解,无须放在文档注释语法(/**...**/)中,而是直接用于修饰程序中的程序单元,如方法、类、接口等等*****
16.2.3 @SuppressWarnings 抑制编译器警告
@SuppressWarnings指示被该Annotation修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings会抑制作用于该程序元素的所有子元素。例如,使用@SuppressWarnings修饰某个类取消显示某个编译器警告,同时又修饰该类中的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。@SuppressWarnings注释的主要功能是用来压制警告,例如,之前讲解泛型操作时,如果在一个类声明时没有指明泛型,则肯定在编译时产生,那么此时就可以使用@SuppressWarnings压制住这种警告。
在通常情况下,如果程序中使用没有泛型限制 的集合将会引起编译器警告,为了避免这种编译器警告,可以使用@SuppressWarnings修饰。
package org.forfan06.annotationdemo; class Demo<T>{ private T var; public T getVar(){ return var; } public void setVar(T var){ this.var = var; } } public class SuppressWarningsAnnotationDemo01{ @SuppressWarnings(value="unchecked") public static void main(String args[]){ Demo d = new Demo(); d.setVar("forfan06"); System.out.println("Content is:" + d.getVar()); } }
程序在声明Demo对象时,并没有指定具体的泛型类型。如果没有@SuppressWarnings注释修饰的话,会出现一下警告信息:
Note: /judge/data/20140904/1409817887476_java_34546/SuppressWarningsAnnotationDemo01.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
但是由于使用了@SuppressWarnings注释,所以此警告信息将不会出现。
在@SuppressWarnings注释中的unchecked,表示的是不检查。当然,如果现在需要压制更多地警告信息,可以在后面继续增加字符串,只是在增加时,要按照数组的格式增加。
范例:压制多个警告
package org.forfan06.annotationdemo; @Deprecated class Demo<T>{ private T var; public T getVar(){ return var; } public void setVar(T var){ this.var = var; } } public class SuppressWarningsAnnotationDemo02{ @SuppressWarnings({"unchecked", "deprecation"}) public static void main(String args[]){ Demo d = new Demo(); d.setVar("forfan06"); System.out.println("Content is:" + d.getVar()); } }上面程序同时存在了泛型和不建议使用方法两种警告信息,但是由于使用了@SuppressWarnings注释,所以此时程序在编译时将不会出现任何的警告信息。
@SuppressWarnings中的关键字如下表所示:
关键字 | 描述 |
deprecation | 使用了不赞成使用的类或方法时的警告 |
unchecked | 执行了未检查的转换时警告。例如,泛型操作中没有指定泛型类型 |
fallthrough | 当使用switch操作时case后未加入break操作,而导致程序继续执行其它case语句时出现的警告 |
path | 当设置了一个错误的类路径、源文件路径时出现的警告 |
serial | 当在可序列化的类上缺少serialVersionUID定义时的警告 |
finally | 任何finally子句不能正常完成时的警告 |
all | 关于以上所有情况的警告 |
范例:另外一种形式的@SuppressWarnings
package org.forfan06.annotationdemo; @Deprecated class Demo<T>{ private T var; public T getVar(){ return var; } public void setVar(T var){ this.var = var; } } public class SuppressWarningsAnnotationDemo03{ @SuppressWarnings(value = {"unchecked", "deprecation"}) public static void main(String args[]){ Demo d = new Demo(); d.setVar("forfan06"); System.out.println("Content is:" + d.getVar()); } }
16.2.4 @SafeVarargs与Java 7的“堆污染”
在泛型擦除时,下面代码可能导致运行时异常List list = new ArrayList<Integer>(); list.add(20); //添加元素时引发unchecked异常 //下面代码引起“未经检查的转换”的警告,编译、运行时完全正常 List<String> ls = list; //*** //但是只要访问ls里的元素,则会引用运行时异常 System.out.println(ls.get(0));
Java把引发这种错误的原因成为 “ 堆污染 ”(Heap Pollution) ,当把一个不带泛型的对象赋值给一个带泛型的变量时,往往就会发生这种 “ 堆污染 ”。
对于形参个数可变的方法,该形参的类型又是泛型,这将更容易导致 “ 堆污染 ”。
public class ErrorUtils{ public static void faultyMethod(List<String>... listStrArray){ //Java语言不允许创建泛型数组,因此listArray只能被当成List[]处理 //此时相当于把List<String>赋给了List,已经发生了 “ 堆污染 ” List[] listArray = listStrArray; //Line5 List<Integer> myList = new ArrayList<Integer>(); myList.add(new Random().nextInt(100)); //把listArray的第一个元素赋为myArray listArray[0] = myList; String s = listStrArray[0].get(0); } }
上面程序代码中Line5处发生了 “ 堆污染 ” 。由于该方法有个形参是List<String>...类型,个数是可变的形参相当于数组。但是,Java又不支持泛型数组,因此程序只能把List<String>...当成List[]处理。这就发生了 堆污染。
如果,不希望看到这个警告,可以使用如下3种方式来 “压制”这个警告:
使用@SafeVarargs修饰引发该警告的方法或构造器
使用@SuppressWarnings("unchecked")修饰
编译时使用-Xlint:varargs选项
第3种方式一般比较少用,通常选择第1种或第2种方式,尤其是使用@SafeVarargs修饰引发该警告的方法或构造器,它是Java 7专门为压制“堆污染”警告提供的。
16.3 JDK的元Annotation
JDK除了在java.lang下提供4个基本的Annotation之外,还在java.lang.annotation包下提供了4个Meta Annotation(元Annotation),这4个元Annotation都用于修饰其他的Annotation定义。16.3.1 @Retention
@Retention只能用于修饰一个Annotation定义,用于指定被修饰的Annotation可以保留多长时间(保存范围)。 @Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值//@Retention的定义: @Documented @Rentention(value=RUNTIME) @Target(value=ANNOTATION_TYPE) public @interface Retention{ RetentionPolicy value(); }
Retention定义中的RetentionPolicy变量用于指定Annotation的保存范围。 其包含一下3个范围:
RetentionPolicy.CLASS: 编译器将把Annotation记录在class文件中。当运行Java程序时,JVM不再保留Annotation。********此Annotation类型将保留在程序源文件(*.java)和编译之后的类文件(*.class)中。在使用此类时,这些Annotation信息不会被加载到虚拟机(JVM)中。如果一个Annotation声明时没有指定范围,则默认是此范围********
RetentionPolicy.RUNTIME: 编译器将把Annotation记录在class文件中。当运行Java程序时,JVM也会保留Annotation,程序可以通过反射获取该Annotation信息。********此Annotation类型的信息保留在源文件(*.java)、类文件(*.class)中,在执行时也会加载到JVM中********
RetentionPolicy.SOURCE: Annotation只保留在源代码中,编译器直接丢弃这种Annotation。 *******此Annotation类型的信息只会保留在程序源文件(*.java)中,编译之后不会保存在编译好的类文件(*.class)中*********
==========如果Annotation里只有一个value成员变量,使用该Annotation时可以直接在Annotation后的括号里面指定value成员变量的值,无须使用name=value形式=======
Java内建的Annotation的范围:
@Override定义采用的是@Retention(value=SOURCE),只能在源文件中出现
@Deprecated定义采用的是@Retention(value=RUNTIME),可以在执行时出现
@SuppressWarnings定义采用的是@Retention(value=SOURCE),只能在源文件中出现
范例:定义在RUNTIME范围有效的Annotation
package org.forfan06.annotationdemo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(value=RetentionPolicy.RUNTIME) //此Annotation在执行时起作用 public @interface MyDefaultRetentionAnnotation{ public String name() default "forfan06" ;// 只能设置枚举的取值 }
上面定义的Annotation在程序执行时起作用,这是一种比较常见的使用方式,而如果此时将其设置成其他范围,则以后再Annotation的应用中肯定是无法访问到的。
要想让一个Annotation起作用,必须结合Java中的反射机制。
16.3.2 @Target
16.3.3 @Documented
16.3.4 @Inherited
16.4 自定义Annotation
16.4.1 定义Annotation
16.4.2 提取Annotation信息
16.4.3 使用Annotation的示例
16.5 编译时处理Annotation
16.6 通过反射取得Annotation
16.6.1 取得全部的Annotation
16.6.2 取得指定的Annotation中的内容
16.7 本章要点
16.9 习题
相关文章推荐
- 注释(@,annotation)在JAVA在用法
- Java1.5 new feature --- Annotation 注释
- Java中Annotation(注释)系列学习笔记(2)
- Java中Annotation(注释)系列学习笔记(1)
- 复习java第五天(枚举、Annotation(注释) 概述)
- 疯狂JAVA讲义---第十四章:Annotation(注释)
- Java注释(Annotation)详解
- 黑马程序员--Java基础学习之内省(IntroSpector)、注释(Annotation)
- Java Annotation注释语法
- Java中Annotation(注释)系列学习笔记(3)
- Java Annotation注释语法
- java中的Java5.0 的注释 (Annotation)、多线程包1(J2SE入门25)
- java annotation 注释 简介
- Java中Annotation(注释)系列学习笔记(1)
- Java Annotation 注释语法
- Java Annotation注释语法
- Java Annotation注释语法[转】
- Java Annotation注释语法(元注释)
- Java中Annotation(注释)系列学习笔记(4)
- Java中Annotation(注释)系列学习笔记(4)