Ray_xujianguo

导航

 

  从JDK1.5开始,Java就增加了Annotation这个新的功能,这种特性被称为元数据特性,同时也被称为注释。

  系统内建的Annotation:

  提醒:以下这三个系统内建的Annotation位于java.lang包下

  1.@Override,相信大家对这个比较熟悉,如果我们要重写一个类的方法的时候,要加上这个注解,但是很多人会反问,不加也是没问题的,但是我们必须考虑到的是程序的正确性,如果你本身的意图是重写这个方法,但是你在写的时候把方法名写错了,那么这就不是重写了,也改变了意图,所以在重写的方法上加上这个注解是防患于未然,也是明确的告诉别人我这个方法是重写的,如何我写错方法名,那么这个注解也会提醒我。

  2.@Deprecated,表示这个程序元素很危险或者存在更好的选择,不鼓励采用这种元素。

 

 1 @Deprecated
 2 public class DeprecatedDemo {
 3 
 4     @Deprecated
 5     public static final String name = "xujianguo";
 6     
 7     @Deprecated
 8     public void print() {
 9         System.out.println("This is the deprecated method");
10     }
11 }

  上面这个类说明了@Deprecated注解也是用在类上,成员属性上,还有方法上,这个类用起来不会报错,但会有警告的信息,表示你所用的东西已经过时了。

  3.@SuppressWarnings,表示取消显示指定的编译器警告,通过这个注解我们可以取消一个不必要的警告,如泛型警告和过时警告,通过查看API文档我们可以发现,该Annotation下有一个value的属性,返回值一个String类型的数组,具体为:public abstract String[] value,这个属性其实是一个警告集,里面用放的是@SuppressWarnings可以压制的警告。

  警告集:

关键字 关键字
deprecation 使用了不赞成使用的类或者方法的警告
unchecked 执行了未检查的转换警告,如泛型操作中没有指定泛型
fallthrough 当switch程序块执行到下种情况时没有break语句的警告
path 在类路径、源文件路径等中有着不存在路径时的警告
serial 当在可序列化类上缺少serialVersionUID定义时的警告
finally 任何finally子句不能完成时的警告
all 关于以上所有的警告

  下面演示一个压制deprecatation和unchecked警告的Demo:

 

 1 /**
 2  * 该类实现了序列化接口,若无serialVersionUID会出现警告
 3  * @author Guo
 4  */
 5 @SuppressWarnings({"serial", "unchecked"})
 6 public class SuppressWarningsDemo implements Serializable{
 7     
 8     public static void main(String[] args) {
 9         
10         /**
11          * 没有指定泛型,出现警告
12          * @author Guo
13          */
14         List<String> list = new ArrayList();
15     }
16 }

 

  自定义Annotation:

  定义自己的Annotation非常简单,就像定义一个接口那样:

1 [public] @interface MyAnnotation {
2 
3 }

  格式很简单,一个Annotation可能接收各种参数,就像SuppressWarnings注解那样,里面可以接收一个数组,下面我们介绍一个它参数定义:

  1.基本变量,可以是String类型的,也可以是int类型的,格式:public 类型 变量名();

  2.数组类型,数组的定义格式也是大同小异:public 类型[] 变量名();

  3.枚举类型,通过定义枚举类型,就可以限定注解里面的内容,格式:格式:public enum 变量名();

  4.默认值,在Annotation中写好默认值,在别的类上使用注解的时候就可以不用写值了,格式:public 类型 变量名() default 默认值;

  下面简单演示一下,大家加深一下印象:

 1 enum MyEnum {
 2     xp, win7, linux;
 3 }
 4 
 5 public @interface MyAnnotation {
 6 
 7     public String name() default "xujianguo";
 8     public int age() default 20;
 9     public String[] array();
10     public MyEnum system() default MyEnum.linux;
11 }
12 
13 class Test {
14 
15     @MyAnnotation(name="zyp", age=20, array = {"zhou", "yan", "ping"}, system=MyEnum.win7)
16     public static void main(String[] args) {
17         System.out.println("Just Test");
18     }
19 }

  现在我们要讨论一个问题,你自定义好的就能在JVM跑吗,其实你看看API中系统内建的三个Annotation,它们都使用了一个注解@Retention,这个注解位于java.lang.annotation包下,其实这个包下还有几个Annotation,我们也介绍一个,不过重点的是@Retention:

  1.@Documented,指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。应使用此类型来注释这些类型的声明:其注释会影响由其客户端注释的元素的使用。如果类型声明是用Documented 来注释的,则其注释将成为注释元素的公共 API 的一部分。简单的说就是用了这个注解,你以后用该Annotation的时候在上面加上注释会被记录到文档上。

  2.@Inherited,指示注释类型被自动继承。如果在注释类型声明中存在 Inherited元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。此过程会重复进行,直到找到此类型的注释或到达了该类层次结构的顶层(Object) 为止。如果没有超类具有该类型的注释,则查询将指示当前类没有这样的注释。简单的说就是这个注解相当于extends啊,父类如果有了某个注解,那个这个注解在子类中也是拥有的,通过反射也可以拿到注解上的信息。

  3.@Target,指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。简单的说这个注解就是限制我们的Annotation可以用在什么地方,它有个value属性,是ElementType类型的,ElementType中规定以下几种范围:

范围

描述

ANNOTATION_TYPE

只能用在注释声明上

CONSTRUCTOR

只能用在构造方法上

FIELD

只能用在字段的声明上

LOCAL_VARIABLE

只能用在局部变量的声明上

METHOD

只能用在方法的声明上

PACKAGE

只能用在包的声明上

PARAMETER

只能用在参数的声明上

TYPE

只能用在类、接口、枚举类型上

  提醒:其实这些限制的范围是可以叠加的,例如,你的Annotation想在类或者方法上使用,可以这么写:@Target(ElementType.TYPE, ElementType.METHOD)

  4.@Retention,指示注释类型的注释要保留多久。这个注释有个value的属性,属性的类型为RetentionPolicy,而RetentionPolicy里面有三个常变量,我们一起来看看这三个常变量。

范围 描述
SOURCE 此Annotation的信息只会保存在程序源文件中(java文件),不会保留在编译好的文件中(class文件)
CLASS

此Annotation的信息保留在程序源文件(java文件)和编译好的文件中(class文件),使用此类的时候

Annotation的信息不会被加载到JVM中,如果一个Annotation没有声明使用什么范围,这个就是默认范围。

RUNTIME 此Annotation的信息会保留在源文件、类文件中,还会被加载到JVM中

  很明确的看出RUNTIME才是我们想要的菜,因为我们要利用Annotation去获取一些信息,我们也来看看我们系统内建的Annotation会属于哪些呢?@Override采用的是Retention(value=RetentionPolicy.SOURCE),@Deprecated采用的是Retention(value=RetentionPolicy.RUNTIME),@SuppressWarnings采用的也是Retention(value=RetentionPolicy.SOURCE),总结一下,一个能真正对我们来说起作用的Annotation应该这样定义:

1 @Retention(value=RetentionPolicy.RUNTIME)
2 public @interface MyAnnotation {
3 
4     public String name() default "xujianguo";
5     public int age() default 20;
6     public String[] array();
7     public MyEnum system() default MyEnum.linux;
8 }

   

  Annotation与反射

  说到Annotation的应用,则不会离开反射,在Class类中存在以下几种跟Annotation操作相关的方法:

方法 描述
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null

public Annotation[] getAnnotations()

返回此元素上存在的所有注释
public Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。
public boolean isAnnotation() 判断元素是否表示一个注释
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注释存在于此元素上,则返回 true,否则返回 false

   下面我自定义一个Annotation,用这个Annotation来模仿JUnit的@Test注解,同时自定义的这个Annotation也有属性,要将这个属性的值拿出来:

  自定义的Annotation-TestSimulation:

1 @Retention(value=RetentionPolicy.RUNTIME)
2 public @interface TestSimulation {
3     public String author() default "xujianguo";
4 }

  运用注解的类AnnotationDemo类:

 1 public class AnnotationDemo {
 2 
 3     @TestSimulation(author="zhouyanping")
 4     public void print() {
 5         System.out.println("This is the method of print");
 6     }
 7     
 8     public void say() {
 9         System.out.println("This is the method of say");
10     }
11     
12     @TestSimulation
13     public void coding() {
14         System.out.println("This is the method of coding");
15     }
16 }

  进行反射解析的AnnotationUtil类:

 1 public class AnnotationUtil {
 2 
 3     public static void main(String[] args) throws Exception {
 4         
 5         /**
 6          * 反射类的对象和拿出一个方法组
 7          * @author Guo
 8          */
 9         Class clazz = Class.forName("com.xujianguo.test.AnnotationDemo");
10         Object object = clazz.newInstance();
11         Method[] methods = clazz.getMethods();
12         
13         for(Method method : methods) {
14             
15             /**
16              * 对方法上的注解进行核对
17              * @author Guo
18              */
19             if(method.isAnnotationPresent(TestSimulation.class)) {
20                 
21                 /**
22                  * 拿到指定的Annotation并获取相应的信息
23                  * @author Guo
24                  */
25                 TestSimulation ts = method.getAnnotation(TestSimulation.class);
26                 System.out.println(ts.author());
27                 method.invoke(object);
28             }
29         }
30     }
31 }

 

 

posted on 2013-09-20 17:13  Ray_xujianguo  阅读(747)  评论(0编辑  收藏  举报