一.Annotation是什么:Annotation提供一种机制,将程序的元素如:类,方法,属性,参数,本地变量,包和元数据联系起来。我的理解是,Annotation给这些程序的元素增加额外的信息注释,编译器在编译时根据约定处理JDK内置的基本注释,比如@Override,@Deprecated等。我们也可以在运行时获取这些元素上的信息,然后根据这些信息来执行不同的操作。
二.Annotation作用:1.编写文档,通过代码里标识的元数据生成文档;2.代码分析,通过代码里标识的元数据对代码进行分析;3.编译检查,通过代码里标识的元数据让编译器能实现基本的编译检查;4.使用Annotation部分代替XML文件来配置运行参数。
三.Java内置的Annotation:
1、@Override定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。例如我们为某类重写toString()方法却写成了tostring(),并且我们为该方法添加了@Override注释;代码如下:
public class OverrideDemo { @Override public String tostring() { return super.toString(); } } |
在编译时,会提示以下错误信息:
OverrideTest.java:4: 方法未覆盖其父类的方法 @Override ^1 错误 |
在eclipse中,红叉叉会告诉你,要去掉方法的Override注释
2、@Deprecated定义在java.lang.Deprecated中,此注释可用于修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。使用@Deprecated的示例代码如下:
public class DeprecatedDemo { public static void main(String[] args) { DeprecatedClass.DeprecatedMethod(); } }
class DeprecatedClass { @Deprecated public static void DeprecatedMethod() { // TODO } } |
在编译时,会得到以下提示信息:
注意:DeprecatedDemo.java 使用或覆盖了已过时的 API。 注意:要了解详细信息,请使用 -Xlint:deprecation 重新编译。 |
3、@SuppressWarnings定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了,参数如下:
参数 | 说明 |
deprecation | 使用了过时的类或方法时的警告 |
unchecked | 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型 |
fallthrough | 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告 |
path | 在类路径、源文件路径等中有不存在的路径时的警告 |
serial | 当在可序列化的类上缺少 serialVersionUID 定义时的警告 |
finally | 任何 finally 子句不能正常完成时的警告 |
all | 关于以上所有情况的警告 |
通过上面的表格,你应该了解到每个参数的用意了,下面我就以一个常用的参数unchecked为例,为你展示如何使用@SuppressWarnings注释,示例代码如下:
import java.util.List; import java.util.ArrayList; public class SuppressWarningsDemo { public static List cache = new ArrayList(); //@SuppressWarnings(value = "unchecked") public void add(String data) { cache.add(data); } } |
当我们不使用@SuppressWarnings注释时,编译器就会有如下提示:
注意:SuppressWarningsDemo.java 使用了未经检查或不安全的操作。 注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。 |
下面我们去掉@SuppressWarnings(value="unchecked")这一行的注释符“//”,它会屏蔽编译时的警告信息,这也就是它所要达到的目的。
四.自定义简单的Annotation
定义一个Annotation是采取的是类似于Interface的定义方式:
@Retention这个元注释(meta-annotation)表示我们创建的TestAnnotation这个Annotation将会存储在Class文件中,并在javaVM运行时加载它。Retention描述了这个注释在程序中会保存多久,由RetentionPolicy中的值来指定, RetentionPolicy有三个值:
可以看到,默认情况下是CLASS类型,这种类型会把注释信息保存入.class文件中,但我们是不能在运行时取到注释中的信息的。而SOURCE类型的注释只是用于编译时的检查等,不会保存如.class文件中,RUNTIME类型的注释会被保存入.class文件中,并且在运行时刻能够得到注释中的信息(关于CLASS和RUNTIME两种类型的区别,在后面证明)。@Target指定了TestAnnotation将用来描述的程序中元素的类型(类,方法,属性,参数,本地变量,包和元数据)。
TestAnnotation同时定义了两个成员,name和notice,都是String类型,有一点需要注意的是,如果只有一个成员,推荐这个成员写成value。在使用时,以如图的方式给他们赋值,而且,当只有一个成员时,可以写成@TestAnnotation(“the value”)的形式。(ps:如果不想每次都去给某一些成员赋值,可以对指定的成员赋初值,形式如:【String name() default “tang”】)
使用annotation:
五.运行时解析annotation
1.创建一个TradeMarker注释,其中有name和owner两个值
2.创建一个License注释,其中包含name,notice,redistribute值,还包含了对TradeMarker注释的引用。
3.在类上使用License注释,并使用反射来得到注释中的信息
AnnotatedElement接口位于 java.lang.reflect 程序包中并由Class、Constructor、Field、Method和Package等类实现,所以在程序中,可以使用这些元素的getAnnotation方法得到注释在他们之上的annotation,进而得到annotation的值,那么,这样可以实现根据annotation来进行运行参数的配置。