理解和使用Annotation

来源:互联网 发布:淘宝企业店保证金 编辑:程序博客网 时间:2024/05/16 14:55

JDK1.5之后,引入了元数据的概念,也就是Annotation(注释),其实它是代码里的特殊标记,这些标记可以再编译、类加载、运行时被读取,并执行相应的处理。

元数据的作用:

如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

1.  编写文档:通过代码里标识的元数据生成文档。
2.  代码分析:通过代码里标识的元数据对代码进行分析。
3.  编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。

一、 系统内建的Annotation:

    @Override 覆写的Annotation

注释能实现编译时检查,你可以为你的方法添加该注释,以声明该方法是用于覆盖父类中的方法。如果该方法不是覆盖父类的方法,将会在编译时报错。例如我们为某类重写toString()方法却写成了tostring(),并且我们为该方法添加了@Override注释,则会提示编译错误。
     @Deprecated 不赞成使用的Annotation

其作用是对不应该在使用的方法添加注释,当编程人员使用这些方法时,将会在编译时显示提示信息,不推荐在使用该方法或该类。
    @SuppressWarnings 压制安全警告的Annotation

与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了,参数如下:
deprecation   使用了过时的类或方法时的警告
unchecked  执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型
fallthrough   当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path   在类路径、源文件路径等中有不存在的路径时的警告
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally    任何 finally 子句不能正常完成时的警告
all 关于以上所有情况的警告

在为@SuppressWarnings设置注释信息的时候,是以key-value的形式出现的,所以以上的@SuppressWarnings也可以直接使用,所以@SuppressWarnings可以使用”value={"unchecked","deprecation"}“的方式来设置。

[java] view plain copy
 print?
  1. @Deprecated  
  2. class Demo<T>{  
  3.     private T var ;  
  4.     public T getVar(){  
  5.         return this.var ;  
  6.     }  
  7.     public void setVar(T var){  
  8.         this.var = var ;  
  9.     }  
  10. };  
  11. public class SuppressWarningsAnnotationDemo03{  
  12.     // @SuppressWarnings(value={"unchecked","deprecation"})  
  13.     public static void main(String args[]){  
  14.         Demo d = new Demo() ;  
  15.         d.setVar("沉缘") ;  
  16.         System.out.println("内容:" + d.getVar()) ;  
  17.     }  
  18. };  

上面,我们将 注释掉,编译后,会出现警告提示:

---------- javac ----------
注: SuppressWarningsAnnotationDemo03.Java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
注: SuppressWarningsAnnotationDemo03.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

打开@SuppressWarnings注释,再次编译,发现,警告已被抑制。


二、 自定义Annotation

定义简单的Annotation形式:

[public] @interface Annotation名称{

        数据类型  变量名称();

}

例如:

[java] view plain copy
 print?
  1. public @interface MyDefaultAnnotationNoneParam{  
  2. }  

之后,就可以直接使用@Annotation名称:

[java] view plain copy
 print?
  1. @MyDefaultAnnotationNoneParam  
  2. class Demo  
  3. {  
  4. }  

此时,就表示在Demo类上使用Annotation。

还可以向Annotation中设置变量,使用变量接受参数。

[java] view plain copy
 print?
  1. public @interface MyDefaultAnnotationSingleParam{  
  2.     public String value();  //接受设置的参数  
  3. }  

在使用的时候,必须清楚的指定变量的名称,变量的内容:

[java] view plain copy
 print?
  1. @MyDefaultAnnotationSingleParam("沉缘")  
  2. class Demo  
  3. {  
  4. }  

或者使用明确的标记,表示内容赋给哪个参数:

[java] view plain copy
 print?
  1. @MyDefaultAnnotationSingleParam(value="沉缘")  
  2. class Demo  
  3. {  
  4. }  

以上的参数,是要赋给value属性的。既然可以设置一个参数,则也就可以同时设置多个参数。

[java] view plain copy
 print?
  1. public @interface MyDefaultAnnotationMoreParam{  
  2.     public String key() ;  
  3.     public String value() ; // 接收设置的内容  
  4. }  

此Annotation在使用时,需要设置两个参数,一个key,一个value。

[java] view plain copy
 print?
  1. @MyDefaultAnnotationMoreParam(key="Linkage",value="沉缘")  
  2. class Demo{  
  3. };  

当然,我们可以设置数组进去,@SuppressWarnings就使用了数组。

[java] view plain copy
 print?
  1. public @interface MyDefaultAnnotationArrayParam{  
  2.     public String[] value() ;   // 接收设置的内容  
  3. }  

接收内容本身是一个数组类型,要传递数组。

[java] view plain copy
 print?
  1. @MyDefaultAnnotationArrayParam(value={"沉缘","流烬"})  
  2. class Demo{  
  3. };  

以上的定义Annotation都未指定属性的默认值,必须在使用时设置。 其实,也可以直接使用default来定义默认值:

[java] view plain copy
 print?
  1. public @interface MyDefaultAnnotationValue{  
  2.     public String key() default "Linkage" ; // 指定好了默认值  
  3.     public String value() default "沉缘" ;    // 指定好了默认值  
  4. }  

在实际的操作中,对于一个Annotation而言,有时候会固定其取值范围,只能使用固定的几个值。那么这时候实际上就需要依靠枚举:

[java] view plain copy
 print?
  1. public enum MyName{ // 定义枚举类型  
  2.     WUQING,WUYUAN,WULEI ;  
  3. }  
  4.   
  5. public @interface MyDefaultAnnotationEnum{  
  6.     public MyName name() default MyName.WUQING ;    // 指定默认值  
  7. }  


三、 限定注释使用范围Target

当我们的自定义注释不断的增多也比较复杂时,就会导致有些开发人员使用错误,主要表现在不该使用该注释的地方使用。为此,Java提供了一个ElementType枚举类型来控制每个注释的使用范围,比如说某些注释只能用于普通方法,而不能用于构造函数等。下面是Java定义的ElementType枚举:

[java] view plain copy
 print?
  1. package java.lang.annotation;  
  2.   
  3. public enum ElementType {  
  4.   
  5.   TYPE,         // Class, interface, or enum (but not annotation)  
  6.   
  7.   FIELD,        // Field (including enumerated values)  
  8.   
  9.   METHOD,       // Method (does not include constructors)  
  10.   
  11.   PARAMETER,        // Method parameter  
  12.   
  13.   CONSTRUCTOR,      // Constructor  
  14.   
  15.   LOCAL_VARIABLE,   // Local variable or catch clause  
  16.   
  17.   ANNOTATION_TYPE,  // Annotation Types (meta-annotations)  
  18.   
  19.   PACKAGE       // Java package  
  20.   
  21. }  
想要使用ElementType,只需要为注释添加@Target即可:

[java] view plain copy
 print?
  1. @Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })  
  2.   
  3. public @interface TargetTest {  
  4.   
  5. }  

正如上面代码所展示的,我们只允许Greeting注释标注在普通方法和构造函数上,使用在包申明、类名等时,会提示错误信息。

四、 Retention和RetentionPolicy,注释保持性策略

[java] view plain copy
 print?
  1. public enum RetentionPolicy {  
  2.   
  3.   
  4.   SOURCE,// Annotation is discarded by the compiler  
  5.   
  6.   
  7.   CLASS,// Annotation is stored in the class file, but ignored by the VM  
  8.   
  9.   
  10.   RUNTIME// Annotation is stored in the class file and read by the VM  
  11.   
  12. }  


RetentionPolicy的使用方法的简单代码示例如下:

@Retention(RetentionPolicy.RUNTIME)

而,在RetentionPolicy的三个范围中,最需要注意的就是RUNTIME范围,因为在执行的时候起作用。

[java] view plain copy
 print?
  1. import java.lang.annotation.Retention ;  
  2. import java.lang.annotation.RetentionPolicy ;  
  3. @Retention(value=RetentionPolicy.RUNTIME) // 表示此Annotation在运行时有效  
  4. public @interface MyDefaultRententionAnnotation{  
  5. public String name() default "沉缘" ;  
  6. }  

我们看下内建的Annotation的RetentionPolicy:

@Override定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

@Deprecated定义采用的是@Retention(value=RUNTIME),可以在执行时出现。

@SuppressWarnings定义采用的是@Retention(value=SOURCE),只能在源文件中出现。


五、 文档化功能

Java提供的Documented元注释跟Javadoc的作用是差不多的,其实它存在的好处是开发人员可以定制Javadoc不支持的文档属性,并在开发中应用。它的使用跟前两个也是一样的,简单代码示例如下:

[java] view plain copy
 print?
  1. import java.lang.annotation.Documented ;  
  2. @Documented  
  3. public @interface MyDocumentedAnntation{  
  4. <span style="white-space:pre">    </span>public String key() default "Linkage" ;  
  5. <span style="white-space:pre">    </span>public String value() default "沉缘" ;  
  6. }  

成功后,在使用此Annotation的时候,可以增加一些信息进去:

[java] view plain copy
 print?
  1. @MyDocumentedAnntation(key="Baidu",value="www.baidu.com")  
  2. public class SimpleBeanDocumented{  
  3.     /** 
  4.      * 此方法在对象输出时调用,返回对象信息 
  5.      */  
  6.     @MyDocumentedAnntation(key="Xinlang",value="www.sina.com")  
  7.     public String toString(){  
  8.         return "Hello World!!!" ;  
  9.     }  
  10. };  

之后,在生成jdk文档的时候,使用@Document修饰的方法将被注释下来。

六、 标注继承

继承应该是Java提供的最复杂的一个元注释了,它的作用是控制注释是否会影响到子类(一个Annotation是否可以被继承下来),简单代码示例如下:

[java] view plain copy
 print?
  1. package com.test.inheriteddemo ;  
  2. import java.lang.annotation.Retention ;  
  3. import java.lang.annotation.RetentionPolicy ;  
  4. import java.lang.annotation.Documented ;  
  5. import java.lang.annotation.Inherited ;  
  6. @Documented  
  7. @Inherited  
  8. @Retention(value=RetentionPolicy.RUNTIME)  
  9. public @interface MyInheritedAnnotation{  
  10.     public String name() ;  
  11.   
  12. }  

使用该注释,标注一个父类Person:

[java] view plain copy
 print?
  1. package com.test.inheriteddemo ;  
  2. @MyInheritedAnnotation(name="沉缘")  
  3. public class Person{  
  4. };  

按照所解释的,使用Inherited声明的Annotation是可以被子类继承下来的:

[java] view plain copy
 print?
  1. import java.lang.annotation.Annotation ;  
  2. import org.lxh.demo16.inheriteddemo.MyInheritedAnnotation ;  
  3. public class ReflectInheritedDemo{  
  4.     public static void main(String args[]) throws Exception{  
  5.         Class<?> c = null ;  
  6.         c = Class.forName("com.test.inheriteddemo.Student") ;  
  7.         Annotation ann[] = c.getAnnotations() ; // 取得全部的Annotation  
  8.         for(Annotation a:ann){  // 输出  
  9.             System.out.println(a) ;  
  10.         }  
  11.         // 继续取得此Annotation设置的内容  
  12.         if(c.isAnnotationPresent(MyInheritedAnnotation.class)){  
  13.             MyInheritedAnnotation mda = null ;  
  14.             mda = c.getAnnotation(MyInheritedAnnotation.class) ;  
  15.             String name = mda.name() ;  // 取出name的内容  
  16.             System.out.println("name = " + name) ;  
  17.         }  
  18.     }  
  19. }  
0 0