Java注释Annotation

Java注释Annotation

从JDK 5开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注释)。Annotation提供了一种为程序元素设置元数据的方法,程序元素包括修饰包、类、构造器、方法、成员变量、参数、局部变量,从某些方面来看,Annotation就想修饰符一样,可用于程序元素的声明,这些信息被存储在Annotation的”name = value”对中。

需要注意的是, Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据,且不会影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称为APT(Annotation ProcessingTool)。

 

一、Annotation基础

前面已经讲到APT负责提取Annotation里包含的元数据,那么我们先了解一下4个基本Annotation的使用,需要在Annotation前面加上@符号,以修饰符的形式放在程序元素的前面。

4个基本的Annotation如下:

@Override

@Deprecated

@SuppressWarnings

@SafeVarargs

1、@Override——限定重写父类方法

顾名思义,@Override就是用来重写方法的,它只能作用于方法,不能作用于其他程序元素,它可以强制一个子类必须覆盖父类的方法。通常情况下会被省略,例如下面的程序指定了子类Circle的draw()方法必须重写父类方法:

class Shape{

   void draw(){

      System.out.println("画一个形状");

   }

}

class Circle extends Shape{

   //将@Override省略效果相同

   @Override

   void draw() {

      System.out.println("画一个圆形");

   }

}

但是@Override作为一个标识,可以告诉我们该方法在其父类中是不是存在,或者我们需要重写的方法是否跟父类中的方法一致,例如上面的程序,如果我把draw()写成darw(),系统将会报编译错误。

 

2、@Deprecated——标示已过时

用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。例如:

public classTest{

   public static void main(String[] args) {

      //下面调用shape的draw方法时,编译器会给出警告

      new Shape().draw();

   }

}

 

class Shape{

   @Deprecated

   void draw(){

      System.out.println("画一个形状");

   }

}

 

3、@supressWarnings——抑制编译器警告

指示被该Annotation修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。@SupressWarnings会抑制作用于该程序元素的所有子元素,例如,使用@SupressWarnings修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。

在通常情况下,如果程序中使用没有泛型限制的集合,将会引起编译器的警告,为了避免编译器的警告,可以使用@SupressWarnings修饰,例如:

//关闭整个类里的编译器警告

@SuppressWarnings("unchecked")

public class Test{

   public static void main(String[]args) {

      List<String> tempList = new ArrayList();

   }

}

 

4、Java7的“堆污染”警告与@SafeVarargs

要理解堆污染,首先看一个例子:

     List list = new ArrayList<Integer>();

      list.add(10);//添加元素时引发unchecked异常

      //下面代码引起“未经检查的转换”的警告,但编译、运行时完全正常

      List<String> temp = list;

      //但只要访问temp里的元素,就会引起运行时异常

      System.out.println(temp.get(0));

如上所示,将list赋值给temp就造成了所谓的“堆污染”当把一个不带泛型的对象赋值给一个带泛型的变量时,往往就会引发“堆污染”。

对于形参个数可变的方法,如果该形参的类型是泛型,就更容易引发“堆污染”了,例如下面的方法中,相当于把List<String>赋值给了List。该方法首先将listArray与listStrArray指向同一内存地址,然后将整型集合赋值给listArray的第一个元素,再使用listStrArray取出第一个元素时,将引发ClassCastException异常:

  public voidtemp(List<String>... listStrArray){

      List[] listArray = listStrArray;

      List<Integer> tempList = Arrays.asList(99);

      listArray[0] = tempList;

      String s = listStrArray[0].get(0);

}

而我们这里要讲的@SafeVarargs,就是Java7专门用来一直“堆污染”警告而提供的Annotation。除此之外,另一种方法就是我们上面所将的,使用@SuppressWarning(“unchecked”)修饰。

 

二、JDK的元Annotation

你可能会觉得这4个Annotation使用起来不太方便,认为Annotation只有这么几个功能。其实,这只是Annotation的凤毛麟角,如果我们想深入了解Annotation,那就得自定义Annotation。

在讲自定义Annotation之前,首先要说的是元Annotation。因为Annotation并不是程序元素,甚至对其进行增删改也不会影响程序的正常运行。那么如果我们想让Annotation在程序运行时也起作用,该怎么办呢?我们要讲的元Annotation就是来干这个的,它用来修饰其他的Annotation定义,使Annotation具有不同的作用域。

1、使用@Retention

我们在创建自定义Annotation时,可以使用@Retention来修饰这个Annotation,用于指定被修饰的Annotation的作用域。@Retention包含一个RetentionPolicy类型的value成员变量,其值可以使如下3个:

RetentionPolicy.Class:默认值,编译器将吧Annotation存储于class文件中,但运行Java程序时,JVM不会保留Annotation,即只存储但不参与程序运行;

RetentionPolicy.RUNTIME:编译器将吧Annotation存储于class文件中,运行Java程序时,JVM也会保留Annotation,即存储且参与程序运行;

RetentionPolicy.SOURCE:编译器不会存储Annotation到class文件中,运行Java程序时JVM也不会保留Annotation,即不存储不保留。

很多时候我们需要通过反射获取注释信息,所以就需要使用value属性值为RetentionPolicy.RUNTIME的@Retention,例如下面我们定义了一个名为Param的Annotation,并且可以通过反射来获取它的注释信息:

@Retention(RetentionPolicy.RUNTIME)

public @interface Param {

   long id();

   String name();

   int sex() default 1;

}

 

2、使用@Target

@Target也可以用来修饰Annotation定义,它用于指定被修饰的Annotation能用于修饰哪些程序单元,其value值有如下几个:

ElementType.ANNOTATION_TYPE:指定该策略的Annotation只能修饰Annotation;

ElementType.CONSTRUCTOR:只能修饰构造器;

ElementType.FIELD:只能修饰成员变量;

ElementType.LOCAL_VARIABLE:只能修饰局部变量;

ElementType.METHOD:只能修饰方法定义;

ElementType.PACKAGE:只能修饰包定义;

ElementType.PARAMETER:可以修饰参数;

ElementType.TYPE:可以修饰类、接口(包括注释类型)或枚举定义。

下面的例子中,定义Param只能修饰方法:

@Target(ElementType.METHOD)

public @interface Param {

   long id();

   String name();

   int sex() default 1;

}

3、使用@Document

@Document用于指定被它修饰的Annotation类将被javadoc工具提取成文档。如果定义Annotation类时使用了@Document修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。举个例子来说明:

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface Param {

   long id();

   String name();

   int sex() default 1;

}

public class Dulven {

   public static void main(String[]args) { }

   @Param(id=1001001,name="GISirFive")

   public void toPerson() { }

}

用javadoc提取API,打开API后是这样的:

如果在定义Param时不加@Document,就不会显示@Param()

4、使用@Inherited

@Inherited用于指定被其修饰的Annotation定义具有继承性,被该Annotation修饰的类如果有子类,则子类将会自动被该Annotation修饰。举个例子:

@Inherited

public @interface Param {

   long id();

   String name();

   int sex() default 1;

}

@Param(id=110, name="singer")

class Base{  }

 

public class Dulven extends Base{

   public static void main(String[]args) {

   System.out.println(Dulven.class.isAnnotationPresent(Param.class));

   }

}

上面程序运行会输出true。                                              

 

三、自定义Annotation

除了这4个基本的Annotation,细心的程序员可能会发现还有很多未知的Annotation。其实除了这4个基本的Annotation外,大多数都是我们自定义的,我们可以通过自定义Annotation来使代码更通俗易懂。

1、定义Annotation

定义一个新的Annotation与定义一个接口类似,需要使用@interface关键字,例如下面定义了一个名为Param的Annotation,并在Test类中使用它:

public @interface Param {    }

 

@Param

public class Test {

   public static void main(String[]args) {     }

}

在默认情况下,Annotation可用于修饰任何程序元素,包括类、接口、方法等。

如普通方法一样,Annotation还可以带成员变量,Annotation的成员变量在Annotation定义中以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型,例如:

public @interface Param {

   long id();

   String name();

   int sex() default 1;

}

一旦在Annotation里定义了成员变量,使用时就需要为其指定值,当然我们也可以为成员变量指定默认值,默认值制定方法如同上面的sex,其默认值为1。这样我们在使用时就不需要为其指定值了,例如:

@Param(id=1001, name="旺旺")

public class Animal {

   public static void main(String[]args) { }

}

我们可以将Annotation按是否包含成员变量分为两类:

标记Annotation:指没有定义成员变量的Annotation。这种Annotation仅利用自身的存在与否来为我们提供信息,例如@Override、@Deprecated等。

元数据Annotation:指包含成员变量的Annotation,因为它们可以接受更多的元数据。

 

2、提取Annotation信息

当开发者使用Annotation修饰了类、方法、Field等成员之后,正如Annotation的定义所言,Annotaion不能影响程序代码的执行,无论增加、删除Annotation,代码都始终如一的执行。只有通过apt工具对Annotation中的信息进行访问和处理,我们才能让程序中的Annotation在程序运行时起一定的作用。所以这些Annotation不会自己生效,必须由开发者提供相应的工具来提取并处理Annotation信息。

Java使用Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的付接口。Java5在

幕大哥
浏览 2156回答 1
1回答

习惯受伤

你这个应该发在手记~~~
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java