Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象注释里的元数据。
0.注解知识体系
1.基本注解
1)@Override 强制重写父类
@Override用来指定方法覆盖载的,强制一个子类必须覆盖父类方法。@Override只能作用于方法,不能作用于其他应用程序元素。
class OverrideTestFather{
public void show(){
System.out.println("Hello father.");
}
}
public class OverrideTest extends OverrideTestFather{
@Override
public void sh(){
System.out.println("Hello child.");
}
/*
@Override
public void show(){
System.out.println("Hello child.");
}
*/
}
2)@Deprecated 标示已过时
class Person{
@Deprecated
public void show(){
System.out.println("Hello person");
}
}
public class DeprecatedTest {
public static void main(String[] args){
new Person().show();
}
}
3)@SuppressWarnings 抑制编译器警告
@SuppressWarnings指示被该Annotation修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素,例如,使用@SuppressWarnings修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法会同时取消两个编译器警告。
在通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可使用@SuppressWarnings修饰。
import java.util.List;
import java.util.ArrayList;
@SuppressWarnings(value="unchecked")
public class SuppressWarningsTest {
public static void main(String[] args)
{
List<String> ls = new ArrayList();
}
}
4)Java 7堆污染与@SafeVarargs
import java.util.List;
import java.util.ArrayList;
public class SafeVarargsTest {
public static void main(String[] args) {
List ls = new ArrayList<Integer>();
//添加元素时引发unchecked异常
ls.add(23);
//引起“未经检查的转换”警告,编译、运行时完全正常
List<String> list = ls;
//访问时出现运行时异常
System.out.println(list.get(0));
}
}
Java7把引发错误的原因称为“堆污染”,当把一个不带泛型的对象赋给一个带泛型的变量时,往往会引发堆污染。程序会发出堆污染警告,保证开发者“更早”注意到程序可能存在的“漏洞”。
有些时候不喜欢看到警告,可以用以下3种方式来“抑制”警告:
(1)使用@SafeVarargs修饰引发该警告的方法或构造器。
(2)使用@SuppressWarnings("unchecked")修饰。
(3)编译时使用-Xlint:varargs选项。
其@SafeVarargs是Java7专门为抑制“堆污染”警告提供的。
2.元注解
1)@Retention
@Retention只能用于修饰一个Annotation定义,用于指定被修饰的Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。
value成员变量的值只能是如下3个:
(1)RetentionPolicy.CLASS:编译器将把Annotation记录在class文件中。当运行Java程序时JVM不再保留Annotation。这是默认值。
(2)RetentionPolicy.RUNTIME:编译器将把Annotation记录在class文件中。当运行Java程序时,JVM也会保留Annotation,程序可以通过反射获取该Annotation信息。
(3)RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器直接丢弃这种Annotation。
如果需要通过反射获取注释信息,就需要使用value属性值为RetentionPolicy.RUNTIME的@Retention。使用@Retention元数据Annotation可采用如下代码来指定值
//定义下面的Testable 法证注解保留到运行时
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Testable{}
如果Annotation里只有一个value成员变量,使用该Annotation时可以直接在Annotation后的括号里指定value成员变量的值,无须使用name=value的形式。
2)@Target
@Target也只能修饰一个Annotation定义,用于指定被修饰的注释能用于修饰哪些程序单元。@Target元Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个值:
(1)ElementType.ANNOTATION_TYPE:指定该策略注解只能修饰Annotation。
(2)ElementType.CONSTRUCTOR:指定该策略的注解只能修饰构造器。
(3)ElementType.FIELD:指定该策略的注解只能修饰字段。
(4)ElementType.LOCAL_VARIABLE:该策略只能修饰局部变量。
(5)ElementType.METHOD:指定该策略都只修饰方法定义。
(6)ElementType.PACKAGE:指定该策略只能修饰包定义。
(7)ElementType.PARAMETER:指定该策略可以修饰参数。
(8)ElementType.TYPE:指定该策略可以修饰类、接口(包括注释类型)或枚举定义。
与使用@Retention类似,使用@Target也可直接在括号里指定value值。如下代码指定@ActionListenerFor 注解只能修饰成员变量。
@Target(ElementType.FIELD)
public @interface ActionListenerFor{}
3)@Documented
@Documented用于指定被该元注解修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含此Annotation的说明。
4)@Inherited
@Inherited元Annotation指定被它的Annotation将具有继承性,如果某些类使用了@A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类自动被@A修饰。此处由下列程序可以看出:
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)//因为是要通过反射验证,所以此处的@Retention设置为RUNTIME。
@Target(ElementType.TYPE)
@Inherited
@interface Inheritable{
}
@Inheritable
class BaseClass{
}
public class InheritedTest extends BaseClass{
public static void main(String[] args) {
boolean isPresent = InheritedTest.class.isAnnotationPresent(Inheritable.class);
System.out.println(isPresent);
}
}
3.自定义注解
1)定义Annotation
public @interface Test{
}
默认情况下,Annotation可用于修饰任何程序元素,包括元素、接口、方法等。
Annotation不仅可以是这种简单的Annotation,还可以带成员变量,Annotation的成员变量在Annotation定义中以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。如下代码:
public @interface AnnotationTest{
//定义两个成员变量Annotation
//Annotation中的成员变量以方法形式定义
String name();
int age();
}
也可以在定义Annotation的成员变量时为其指定默认值,指定成员变量的初始值可使用default关键字。如下代码就是使用了default指定初始值。
public @interface AnnotationTest{
String name() default "tad";
int age() default 23;
}
如果为Annotation指定了默认值,使用该Annotation时则可以不为这些成员变量指定值。
@interface定义的Annotation非常像定义的注释接口,继承了Annotation接口。
2)使用Annotation
使用Annotation可以修饰程序中的类、方法、字段、接口。通常将注解放在所有修饰符之前,而且由于使用注解时可能还需要为成员变量指定值,因而注解长度可能较长,所以把注解另放一行。如下代码:
@Test
public class Tad{
}
一旦在Annotation里定义了成员变量之后,使用该Annotation时就可以该Annotation变量指定值,如下代码:
public class Test{
@AnnotationTest(name="xxx",age=6)
public void show(){
//
}
}
也可以在定义Annotation的成员变量时为其指定默认值,指定默认值可以使用default关键字。如下代码定义两个成员变量:name和age,这两个成员变量使用default指定了初始值。
public @interface Test{
String name() default "tad";
int age() default 23;
}
如果为Annotation的成员变量指定了默认值,使用该Annotation时则可以不为这些成员变量指定值,而是直接使用默认值。
当然也可以为成员变量指定值,如果成员变量指定了值,则默认值不起作用。
根据Annotation是否可以包含成员变量,可把Annotation分为如下两类:
(1)标记Annotation:一个没有定义成员变量的Annotation类型被称为标记。这种Annotation仅利用自身的存在与否提供信息,如@Override。
(2)元数据Annotation:包含成员变量的Annotation,因为它们可以接收更多的元数据,所以也被称为元数据Annotation。