Java注解Annotation(转)

一、概念  

注解

  Java 中的注解是 Java 源代码的元数据,也就是说注解是用来 Java 源代码的,基本语法就是  @后面跟注解的名称。JDK5.0 以上版本都支持,有预定义注解,也可自定义注解。

Annontation 是 Java5 开始引入的新特征。中文名称一般叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。
更通俗的意思是为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且是供指定的工具或框架使用的。
Annontation 像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

元数据

  所谓元数据是描述数据的数据, 也就是说元数据是描述数据的, 就是数据库表中的字段一样,

每个字段描述了这个字段下的数据含义。

  元数据可用于创建文档、跟踪代码中的依赖性、甚至执行基本编译时的检查。

  许多元数据工具,如:XDoclet,将这些功能添加到 Java 语言中,暂时成为 Java 编程功能

中的一部分。

  一般来说,元数据用途有三:

  1、文档编制;

  2、编译器检查;

  3、代码分析。

  元数据提供了一种有用的方法来指明方法是否取决于其他方法、它们是否完整、特定类是

否必须引用其他类,等等。


二、原理
Annotation 其实是一种接口。通过 Java 的反射机制相关的 API 来访问 annotation 信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。
annotation 是不会影响程序代码的执行,无论 annotation 怎么变化,代码都始终如一地执行。
Java 语言解释器在工作时会忽略这些 annotation,因此在 JVM 中这些 annotation 是“不起作用”的,只能通过配套的工具才能对这些 annontaion 类型的信息进行访问和处理。
Annotation 与 interface 的异同:
1)、Annotation 类型使用关键字 @interface 而不是 interface。
这个关键字声明隐含了一个信息:它是继承了 java.lang.annotation.Annotation 接口,并非声明了一个 interface
2)、Annotation 类型、方法定义是独特的、受限制的。
Annotation 类型的方法必须声明为无参数、无异常抛出的。这些方法定义了 annotation 的成员:方法名成为了成员名,而方法返回值成为了成员的类型。而方法返回值类型必须为 primitive 类型、Class 类型、枚举类型、annotation 类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用 default 和一个默认数值来声明成员的默认值,null 不能作为成员默认值,这与我们在非 annotation 类型中定义方法有很大不同。
Annotation 类型和它的方法不能使用 annotation 类型的参数、成员不能是 generic。只有返回值类型是 Class 的方法可以在 annotation 类型中使用 generic,因为此方法能够用类转换将各种类型转换为 Class。
3)、Annotation 类型又与接口有着近似之处。
它们可以定义常量、静态成员类型(比如枚举类型定义)。Annotation 类型也可以如接口一般被实现或者继承。
三、应用场合
annotation 一般作为一种辅助途径,应用在软件框架或工具中,在这些工具类中根据不同的 annontation 注解信息采取不同的处理过程或改变相应程序元素(类、方法及成员变量等)的行为。
例如:Junit、Struts、Spring 等流行工具框架中均广泛使用了 annontion。使代码的灵活性大提高。
四、常见标准的 Annotation
从 java5 版本开始,自带了三种标准 annontation 类型,
(1)、Override
java.lang.Override 是一个 marker annotation 类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种 annotation 在一个没有覆盖父类方法的方法时,java 编译器将以一个编译错误来警示。
这个 annotaton 常常在我们试图覆盖父类方法而确又写错了方法名时加一个保障性的校验过程。

该注解作用是标识一个方法是否正确的覆写了所在类的父类的方法,可以用来避免写代码时候

的疏忽大意,导致无谓的错误,例:

@java.lang.Override
public void sayHello() {
System.out.println("Hello,world");
}

@Override
public void sayHello() {
System.out.println("Hello,world");


(2)、Deprecated
Deprecated 也是一种 marker annotation。当一个类型或者类型成员使用 @Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
注意:@Deprecated 这个 annotation 类型和 javadoc 中的 @deprecated 这个 tag 是有区别的:前者是 java 编译器识别的,而后者是被 javadoc 工具所识别用来生成文档(包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述)。
(3)、SuppressWarnings
此注解能告诉 Java 编译器关闭对类、方法及成员变量的警告。
有时编译时会提出一些警告,对于这些警告有的隐藏着 Bug,有的是无法避免的,对于某些不想看到的警告信息,可以通过这个注解来屏蔽。
SuppressWarning 不是一个 marker annotation。它有一个类型为 String[] 的成员,这个成员的值为被禁止的警告名。对于 javac 编译器来讲,被 -Xlint 选项有效的警告名也同样对 @SuppressWarings 有效,同时编译器忽略掉无法识别的警告名。
annotation 语法允许在 annotation 名后跟括号,括号中是使用逗号分割的 name=value 对用于为 annotation 的成员赋值:
代码:

1
2
3
4
@SuppressWarnings(value={"unchecked","fallthrough"})
 publicvoidlintTrap() {
  /*sloppy method body omitted */
}

 
在这个例子中 SuppressWarnings annotation 类型只定义了一个单一的成员,所以只有一个简单的 value={...} 作为 name=value 对。又由于成员值是一个数组,故使用大括号来声明数组值。
注意:我们可以在下面的情况中缩写 annotation:当 annotation 只有单一成员,并成员命名为 "value="。这时可以省去 "value="。比如将上面的 SuppressWarnings annotation 进行缩写:
代码:
    @SuppressWarnings("unchecked")
如果 SuppressWarnings 所声明的被禁止警告个数为一个时,可以省去大括号:
    @SuppressWarnings("unchecked")
五、自定义 annontation 示例

最简单的自定义注解:

public @interface MyAnno {
}

内部没有执行代码.

1 public class Hello
2 {
3     @MyAnno
4     public static void main(String[] args)
5     {
6         System.out.println("自定义注解...");
7     }
8 }

 

为自定义注解添加数据:

  注解是用来描述源代码的元数据, 所以经常要为注解提供数据成员. 定义数据成员后

不需要分别定义 getter 和 setter 方法。相反只需要定义一个方法,以数据成员的名称命名它,

数据类型应该是该方法返回值的类型。

public @interface MyAnno {
public String schoolName();
}

  此时注解的使用格式为:

@MyAnno(schoolName = "schoolName")
public static void main(String[] args) {
// TODO Auto-generated method stub
A aRef = new A();
aRef.sayHello();
}

  在使用注解的时候必须为数据成员设置值,除非在定义注解的时候为数据成员设置了默认

值,如下:

public @interface MyAnno {
public String schoolName() default "BUPT";
}

注意:default 后面数据的类型必须与返回类型相同。
这样在使用注解的时候可以不用显示为注解数据成员设置相应的值,这时候该成员将使用默认

值进行初始化:

@MyAnno
public static void main(String[] args) {
// TODO Auto-generated method stub
A aRef = new A();
aRef.sayHello();
}

当注解中需要设置多个值的时候,需要将这多个赋值表达式使用逗号(“,”)隔开, 如下:

定义注解:

复制代码
package cn.edu.bupt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
public String schoolName();
public String studentName();
}
复制代码

注解多值设置:

复制代码
package cn.edu.bupt;
public class Hello {
/**
*
@param args
*/
@MyAnno(schoolName="BUPT",studentName="YangM")
public static void main(String[] args) {
// TODO Auto-generated method stub
A aRef = new A();
aRef.sayHello();
}
}
复制代码

注意注解的定义注解的使用两者的差别。

自定义注解的意义:

  自定义注解都是有目的的,那么如何表示这个目的呢?那就是在定义注解的时候对其进行描

述,也就是注解的元数据,或者说是注解的注解。这输入注解使用的范畴

  1、指定注解应用范围(Target)

  最明显的注解的元数据就是允许何种程序元素能够使用定义过的注解类型。这种元注解被称

为 Target,但在了解如何使用 Target 之前,需要认识 ElementType,实际上它是一个枚举类型

,定义了注解类型可应用的不同的程序元素,如类,成员数据成员方法构造方法方法的参

Java Package局部变量

具体取值有:

   a> CONSTRUCTOR      用于描述构造器

   b> FIELD           用于描述创元字段

   c> LOCAL_VARIABLE     用于描述局部变量

   d> METHOD          用于描述成员方法

   e> PACKAGE         用于描述包

   f> PARAMETER        用于描述参数

   g> TYPE            用于描述类或接口 (甚至 enum)

 

  ElementType 枚举类型的枚举值意义很明确,使用 Target 元注解时,至少要提供这些枚举值

中的一个,并指出定义的目标注解所应用的程序元素。

  例如:

package cn.edu.bupt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
public String schoolName();
}

这说明,我们定义的 MyAnno 注解可以应用在类上、接口上、枚举上、成员方法上,如果没有定义

ElementType.METHOD 应用在方法上将会报错。

通过指定 Target 元注解, 就能防止误用自定义注解。

注意:设定多个枚举值时, 需要使用花括号包围他们, 并且使用逗号 (",") 分隔;当设定一个枚举值时,

可不适用花括号,仅仅小括号即可。

 2、设置保持性 Retention

  这个元注解和 Java 编译器处理注解的注解类型方式有关,编译器有几种不同选择:

  a>  按照规定使用注解,但是并不将它保留到编译后的.class 文件中,即源代码级别的;(SOURCE)

  b>  将注解保留在编译后的.class 文件中,但在运行时忽略它,字节码级别的;(CLASS)

  c>  将注解保留在编译后的.class 文件中,并在第一次加载类的时候读取它。(RUNTIME)

   默认都是 RUNTIME。

  这三种选择用 java.lang.annotation.RetentionPolicy 枚举表示,如下所示:

package java.lang.annotation
public enum RetentionPolicy {
SOURCE, //Annotation is discarded by the compiler
CLASS, //Annotation is stored in the class file,but ignored by the VM
RUNTIME //Annotation is stored in the class file,and read by the VM
}

例如,使用 RetentionPolicy

复制代码
package cn.edu.bupt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
public String schoolName();
public String studentName();
}
复制代码

 

   3、添加公共文档 Documented

  在默认情况下,使用 javadoc 自动生成文档时。注解将被忽略掉,如果想在文档中包含注解

必须使用 Documented 为文档注解,如下:

复制代码
package cn.edu.bupt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
public String schoolName();
public String studentName();
}
复制代码

 

  4、设置继承  Inherited

  在默认情况下,类的继承过程,父类中的注解并不会被子类继承,如果需要继承,需要加上

@Inherited

例如:

复制代码
package cn.edu.bupt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
public String schoolName();
public String studentName();
}
复制代码

 


示例共涉及四个类:
清单 1:Author.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packagecom.magc.annotation;
importjava.lang.annotation.Documented;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
 
/*** 定义作者信息,name和group
   * @authormagc
   **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public@interfaceAuthor {
String name();
String group();
}

 


清单 2:Description.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
packagecom.magc.annotation;
   
 importjava.lang.annotation.Documented;
 importjava.lang.annotation.ElementType;
 importjava.lang.annotation.Retention;
 importjava.lang.annotation.RetentionPolicy;
 importjava.lang.annotation.Target;
   
/***
 *@authormagc
 * 定义描述信息 value*/@Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 @Documented
 public@interfaceDescription {
 String value();
 }

 


清单 3:Utility.java

1
2
3
4
5
6
7
8
packagecom.magc.annotation;<BR>  
@Description(value ="这是一个有用的工具类")
public class Utility {
@Author(name ="haoran_202",group="com.magc")
public String work() {
 return"work over!";
}
 }

 


注:这是个普通的 Java 类,运行了 @Description 和 @Author 注解。
清单 3:AnalysisAnnotation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
packagecom.magc.annotation;
 
importjava.lang.reflect.Method;
 
public class AnalysisAnnotation {
/*** 
 *在运行时分析处理annotation类型的信息
   **/
public static void main(String[] args) {
try{//通过运行时反射API获得annotation信息
Class rt_class =Class.forName("com.magc.annotation.Utility");
Method[] methods =rt_class.getMethods();
booleanflag =rt_class.isAnnotationPresent(Description.class);
 
if(flag) {
Description description =(Description)rt_class.getAnnotation(Description.class);
System.out.println("Utility's Description--->"+description.value());
for(Method method : methods) {
if(method.isAnnotationPresent(Author.class)) {
Author author =(Author)method.getAnnotation(Author.class);
System.out.println("Utility's Author--->"+author.name()+"from "+author.group());
    }
   }
 }
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}

 
注:这是个与自定义 @Description 和 @Author 配套的基础框架或工具类,通过此类来获得与普通 Java 类 Utility.java 关联的信息, 即描述和作者。
运行 AnalysisAnnotation, 输出结果为:
Utility's Description---> 这是一个有用的工具类
Utility's Author--->haoran_202 from com.magc

 

原文链接:http://www.cnblogs.com/mandroid/archive/2011/07/18/2109829.html