注解

来源:互联网 发布:ubuntu 退出图形界面 编辑:程序博客网 时间:2024/06/05 21:12

Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象注释里的元数据。


0.注解知识体系


Java注解知识体系如下图所示,系统掌握注解。


1.基本注解


1)@Override 强制重写父类

@Override用来指定方法覆盖载的,强制一个子类必须覆盖父类方法。@Override只能作用于方法,不能作用于其他应用程序元素。

class OverrideTestFather{public void show(){System.out.println("Hello father.");}}public class OverrideTest extends OverrideTestFather{@Overridepublic void sh(){System.out.println("Hello child.");}/*@Overridepublic void show(){System.out.println("Hello child.");}*/}


2)@Deprecated 标示已过时

@Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过的类、方法时,编译器将会给出警告。
class Person{@Deprecatedpublic 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.元注解

java.lang包提供了4个基本的Annotation,在java.lang.annotation包下提供了4个MetaAnnotation(元Annotation),此4个Annotation都用于修饰其他的Annotation定义。

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{}@Inheritableclass 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

定义新的注解类型使用@interface关键字,与定义接口非常像。
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可以修饰程序中的类、方法、字段、接口。通常将注解放在所有修饰符之前,而且由于使用注解时可能还需要为成员变量指定值,因而注解长度可能较长,所以把注解另放一行。如下代码:

@Testpublic 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。


3)提取Annotation

当使用Annotation修饰了类、方法、字段后,Annotation不会自己生效,必须由开发者提供相应的工具来提取并处理Annotation信息。
Java使用Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的父接口。Java 5在java.lang.reflect包下新增AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素。
该接口主要有如下实现类:
(1)Class:类定义
(2)Constructor:构造器定义
(3)Field:类的成员变量定义
(4)Method:类的方法定义
(5)Package:类的包定义
java5开始java.lang.reflect包所提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类被定义为运行时Annotation后,该Annotation才会在运行时可见,JVM才会在装载.class文件时读取保存在class文件中的Annotation。
AnnotatedElement接口是所有程序元素(如Class、Method、Constructor等)的父接口,所以程序通过反射获取某个类的AnnotatedElement对象(如Class、Method、Constructor等)之后,程序就可以调用该对象的如下3个方法来访问Annotation的信息。
(1)getAnnotation(Class<T> annotationClass):返回该程序元素上存在的指定类型的注释,如果该类型的注释不存在,返回null。
(2)Annotation[] getAnnotation():返回程序元素上存在的所有注释。
(3)boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断程序元素上是否存在指定类型的注释,如果存在则返回true,否则返回false。

4.处理注解

APT:Annotation Processing Tool,是处理注解的工具,从源码文件中检测出其中的Annotation,对Annotation进行处理。
Annotaion处理器处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它文件(文件具体内容也Annotation决定),APT还会编译生成的源代码文件和原来的源文件,一起生成class。
使用APT主要目的是简化开发者的工作量,因为APT可以在编译程序源代码时生成一些附属文件(比如源文件、类文件、程序发布描述文件),这些附属文件的内容也与源代码有关。
java的javac.exe工具中的-processor选项,该选项可指定一个Annotation处理器,如果在编译Java源文件时通过选项指定了Annotation处理器,那么该处理器将会在编译时提取并处理源文件中Annotation。
每个Annotation处理器都需要实现javax.annotation.processing包下的Processor接口。实现该接口必须实现它里的所有方法,通常会采用继承AbstractProcessor的方式来实现Annotation处理器。

0 0
原创粉丝点击