1.Annotation的含义
Annotation主要服务于编译器,属于一种配置信息。在本质上,Annotation提供了一种与程序元素关联任何信息或者任何元数据的方式。Annotation可以像修饰符一样被使用,可以应用于任何程序元素(如包、类型、构造方法、方法、成员变量、参数、本地变量)的声明中。这些信息被存储在Annotation的“name=value”结构对中。事实上,Annotation类型是一种接口,能够通过java发射API(应用程序接口)的方式提供对其信息的访问。
2.系统内建的Annotation
JDK1.5之后的系统中,内建有3个Annotation:@Override、@Deprecated、@SuppressWarnings。
2.1@Override
@override表示方法覆写的正确性,例如,如下代码:
package 类集; class Person{ public String getInfo(){ // 取得信息 return "这是一个Person类。" ; } }; class Student extends Person{ // 继承此类 @Override public String getinfo(){ // 覆写方法,这里覆写的方法大小写错误了,就会报错。 return "这是一个Student类。" ; } }; public class GetInterfaceDemo { public static void main(String[] args) throws Exception { Person per = new Student() ; System.out.println(per.getInfo()) ; // 输出信息 } } |
如果没有上述@override,则方法名称getInfo编写错误,大小写错误。这个时候为了保证覆写正确性,添加覆写属性,保证程序正确执行。
2.2@Deprecated
使用Deprecated注释的Annotation本身是不建议使用的操作。
package 类集; class Demo{ @Deprecated // 声明不建议使用的操作 public String getInfo(){ return "这是一个Person类。" ; } }; public class GetInterfaceDemo { public static void main(String[] args) throws Exception { Demo d = new Demo() ; System.out.println(d.getInfo()) ; } } |
以上程序并不会使得编译出差,但是会产生一个警告信息。
2.3@SuppressWarnings
用于压制警告信息。以泛型操作为例,在泛型中如果没有指定泛型类型,则使用时候肯定出现安全警告。
正常情况不使用annotation。没有指定泛型类型。
package 类集; class Demo<T>{ private T var ; public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } }; public class GetInterfaceDemo { public static void main(String args[]){ Demo d = new Demo(); //因为没有给泛型指定类型,这里会有警告 d.setVar("小华"); //这里会有警告 System.out.println("内容:" + d.getVar()) ; } } |
此时可以使用SuppressWarning这个Annotation将这种警告信息进行压制。注释信息表示要强制压制的信息,可以通过@SuppressWarnings(“信息”)方式进行注释,里面的信息可以用数组形式注释,例如:@SuppressWarnings({ "unchecked", "rawtypes" })。
package 类集; class Demo<T>{ private T var ; public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } }; public class GetInterfaceDemo { @SuppressWarnings({ "unchecked", "rawtypes" }) public static void main(String args[]){ Demo d = new Demo() ; d.setVar("小华") ; System.out.println("内容:" + d.getVar()) ; } } |
3.自定义Annotation
自定义Annotation的语法如下
[public] @interface Annotation的名称{ [数据类型 变量名称();] } |
在程序中只要使用了@interface声明Annotation,那么此Annotation实际上就相当于继承了java.lang.annotation.Annotation接口。例如:
@interface MyAnnotation{ public String key(); public String value(); } public class TestAnnoVar{ @MyAnnotation(key=”var1”,value=”test”) public static void main(String[] arg){ System.out.println(“Hello World”); } } |
如果在自定义Annotation中声明变量,且没有设置默认值,则在使用该Annotation时必须为变量赋值。如下为自定义Annotation中变量设置为默认值的例子:
@interface MyAnnotation{ public String key() default “var1”; public String value() default “test”; } public class TestAnnoVar{ @MyAnnotation() public static void main(String[] arg){ System.out.println(“Hello World”); } } |
4.Retention和RetentionPolicy
在注解中,可以使用Retention(中文含义为“保留”)来定义一个Annotation的保存范围,定义如下:
@Documented @Retention(value=RUNTIME) @Target(value=ANNOTATION) public @interface Retention{ RetentionPolicy value(); } |
其中,@Documented有注解的作用,将自定义注解设置为文档说明信息。
RetentionPolicy是一个枚举类型,用于指定Annotation的保存范围。RetentionPolicy包含3个取值范围:
- SOURCE:此Annotation类型的信息只会记录在源文件中,编译时将会被编译器丢弃,及此Annotation信息不会保存在编译号的类文件中。
- CLASS:编译器将会把注释记录在类文件中,但不会被加载到JVM中。如果一个Annotation声明时没有指定范围,则系统默认值CLASS。
- RUNTIME:此Annotation类型的信息将会被保留在源文件、类文件中,在执行时也会加载到java虚拟机中,因此可以反射性读取。
5.反射与Annotation
注解通过反射,可以取得在一个方法上声明的全部内容。
在Filed、Method、Constructor的父类上定义了以下与Annotation发射操作相关的方法。
- 取得全部的Annotation;
- 判断操作的是否是指定的Annotation。
5.1取得全部的Annotation
例如下面代码:
import java.lang.annotation.Annotation; import java.lang.reflect.Method; class Info { @Override @Deprecated @SuppressWarnings( value = "" ) public String toString() { return "Hello World!"; } } public class GetAnnotations { public static void main( String[] args ) throws Exception { Class<?> cls = Class.forName( "Info" ); Method toStringMethod = cls.getMethod( "toString" ); // 取得全部的Annotation Annotation ans[] = toStringMethod.getAnnotations(); for( int i=0; i<ans.length; ++i ) { System.out.println( ans[i] ); } } } |
上述代码3个内建的Annotation中只有@Deprecated是RUNTIME类型。所以只输出了Deprecated。也就是说,只有定义采用的是@Retention(value=RUNTIME)的Annotation才能在程序运行时被反射机制取得。
5.2取得指定的Annotation
例如下面代码:
import java.lang.annotation.*; import java.lang.reflect.Method; @Retention(value = RetentionPolicy.RUNTIME) @interface MyAnnotation { public String key(); public String value(); } class Info { @Override @Deprecated @SuppressWarnings( value = "" ) @MyAnnotation( key = "one", value = "test" ) public String toString() { return "hello"; } } public class GetAnnotation { public static void main( String[] args ) throws Exception { Class<?> cls = Class.forName( "Info" ); Method toStringMethod = cls.getMethod( "toString" );
// 判断该方法上是否有指定类型的Annotation存在 if( toStringMethod.isAnnotationPresent( MyAnnotation.class ) ) { MyAnnotation my = null; // 声明Annotation的对象 my = toStringMethod.getAnnotation( MyAnnotation.class ); String key = my.key(); String value = my.value(); System.out.println( key + " --> " + value ); } } } |
6.深入Annotation
在java.lang.annotation包下还有:Target、Documented、Inherited。下面分别进行介绍。
6.1Target
@Target明确指出了一个Annotation的使用位置。在@Target中存在一个ElementType[]枚举类型的变量,这个变量主要用于指定Annotation的使用权限。ElementType类定义了下面8个取值:
(1)ANNOTATION_TYPE:只能用在注释上;
(2)CONSIRUCTOR:只能用在构造方法的声明上;
(3)FOELD:只能用在字段的声明(包括枚举常量)上;
(4)LOCAL_VARIABLE:只能用在局部变量的声明上;
(5)METHOD:只能用在方法的声明上;
(6)PACKAGE:只能用在包的声明上;
(7)PARAMEIER:只能用在参数的声明上;
(8)TYPE:只能用在类、接口、枚举类型上。
如果要为value设置多个值,可以使用下面的方法:
value={ElementType.TYPE,ElementType.METHOD} |
6.2Documented注释
@Documented可以将自定义的注释设置为文档说明设置信息,因此在生成javadoc时就会将该注解加入文档。
下面例子使注解可以出现在javadoc文档中:
import java.lang.annotation.*; @Documented @Retention(value = RetentionPolicy.RUNTIME) public @interface MyAnnotation { public String key(); public String value(); } class Info { @Override @Deprecated @SuppressWarnings(value = "") @MyAnnotation(key = "one", value = "test" ) public String toString() { return "hello"; } } |
6.3Inherited
@Inherited用于标注一个父类的Annotation是否可以被子类继承,如果一个Annotation需要被其子类继承,则在声明时直接使用@Inherited注释即可。
import java.lang.annotation.*; @Inherited @Documented @Retention( value = RetentionPolicy.RUNTIME ) public @interface InheritedAnnotation { } @InheritedAnnotation class Person { } class Student extends Person { } |
虽然Student类上并没有直接使用InheritedAnnotation,但是却从Person类上继承了下来,所以可以通过反射极值取出该Annotation。
注意点:
1.想配合反射机制就必须设置@Retention(value=RetentionPolicy.RUNTIME)。
2.@Target可以指定一个Annotation的使用范围。
3.如果一个annotation希望被使用类的子类所继承,则要使用@Inherited Annotation。