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

努力奋斗的小墨鱼 ---- http://www.cnblogs.com/WayneZeng/
原文地址:https://www.cnblogs.com/WayneZeng/p/2699705.html