Java注解
从Java5
开始,Java增加对元数据的支持,也就是Annotation
,不是一般的注释。这些标记在编译、类加载、运行时被读取,并执行相应处理。通过使用注解,开发人员在源文件中嵌入一些补充信息,进而代码分析和部署工具可以通过这些补充信息进行部署。某方面看,Annotation
就像修饰符一样,可用于修饰包、方法和构造器、变量等,这些信息被储存在Annotation
的”name=value”对中。
Annotation
是一个接口,程序可以通反射来获取指定程序元素的Annotation
对象,然后通过注解对象来取得里面的元数据。
基本注解
注解必须使用工具提取元数据,工具还会根据元数据增加额外的功能,这种处理访问注解的工具统称APT
。5个注解都在Java.lang
包下,5个基本注解如下:
- @Override
- @Deprecated
- @SuppressWarnings
- @SafeVarargs(Java7新增)
- @FunctionalInterface(Java8新增)
限定重写父类:@Override
@Override
就是用来指定方法覆盖的,它强制一个子类必须重写父类方法,告诉编译器检查这个方法,避免低级错误。注意的是:@Override
只能修饰方法
,不能修饰其他程序元素!
标识已过时:@Deprecated
该注解表示类、方法、接口
已过时,当其他程序使用已过时的类、方法时,编译器将警告。
抑制编译器警告:@SuppressWarnings
顾名思义,注解让编译器不会发出警告,如果修饰在类上,则该类不会有任何警告出现,如果修饰方法,则该方法不会有任何警告出现。
1 |
|
Java7堆污染警告和@SafeVarargs
上次讲泛型的时候,把一个不带泛型的对象赋给一个带泛型的变量时,将有可能引起转换异常,这种错误的原因可以称为堆污染
。有些时候,开发者不希望看到该警告,可以用以下三种方法抑制警告:
- @SafeVarargs修饰该方法或构造器
- @SuppressWarnings(“unchecked”)
- 编译使用
-Xlint:varargs
Java8的函数式接口与@FunctonalInterface
Java8规定:如果接口中只有一个抽象方法
,该接口就是函数式接口
。而该注解就是用来指定某个接口
必须为函数式接口的,只能含有一个抽象方法
。注意:该注解只能修饰接口!
元注解
JDK除了5个基本注解外,还提供了6个元注解,其中5个都用于修饰其他注解定义,其中的@Repeatable
专门用于定义Java8
新增的重复注解。
@Retention
只能用于修饰注解定义,用于指定注解可以保留多长时间。@Retention
包含一个RetentionPolicy
类型的Value
成员变量,使用时指定值。value
成员变量的值只能是如下三个:
- RetentionPolicy.CLASS:编译器把注解记录在
class
文件中,当运行程序时,JVM不可获取注解信息。默认值! - RetentionPolicy.RUNTIME:编译器把注解记录在
class
文件中,当运行程序时,JVM可以获取注解信息,程序可以通过反射获取注解信息。 - RetentionPolicy.SOURCE:注解只保留在源代码中,编译器之间丢弃。
1 |
|
@Target
只能修饰一个注解定义,用于指定被修饰的注解能用于修饰哪些程序单元。。同样它也包含一个名为value
的成员变量,其值有:
- ElementType.ANNOTATION_TYPE:指定注解只能修饰注解
- ElementType.CONSTRUCTOR:指定该注解只能修饰构造器
- ElementType.FIELD:只能修饰成员变量
- ElementType.LOCAL_VARIABLE:只能修饰局部变量
- ElementType.METHOD:只能修饰方法
- ElementType.PACKAGE:只能修饰包
- ElementType.PARAMETER:修饰参数
- ElementType.TYPE:可以修饰类、接口、注解或枚举定义
同样的操作,在括号内指定value
值,可以省去name=value
形式,直接填入值。
1 | //只能修饰成员变量 |
@Documented
指定修饰的Annotation类
将被javadoc
工具提取成文档,如果定义Annotation类
使用了该注解,则所有使用该Annotation
修饰的程序元素的API文档中将包含该Annotation
说明。
1 |
|
@Inherited
指定被它修饰的Annotation
具有继承性,即某个类使用了该Annotation
,则其子类将自动被该Annotation
修饰。
1 |
|
自定义注解
上面已经提到了自定义注解,跟定义接口差不多,只不过在interface
前添加@符号。通常自定义注解可以修饰任何程序元素,比如类、接口、方法等。然而我们可以根据Annotation
是否包含成员变量,可以分为两类:
- 标记注解:没有成员变量,仅提供标记信息。
- 元数据注解:包含成员变量的
Annotation
,它们可以接收更多的元数据。
一旦在注解内定义成员变量后,使用时就应该指定成员变量的值,或者直接在注解内指定默认值,可使用default
关键字。
1 | public MyTag{ |
提取注解信息
Java使用Annotation
接口来代表程序元素前的注解,该接口是所有注解的的父接口,同时在java.lang.reflect
包下新增AnnotatedElement
接口,该接口表示可以接收注解的程序元素。该接口的实现类主要有:
- Class:类定义
- Constructor:构造器定义
- Field:类的成员变量
- Method:类的方法
- Package:类的包
包同时还包含一些反射功能的工具类,AnnotatedElement
接口是所有程序元素的父接口,所以程序通过反射获取某个类的AnnotatedElement
对象之后,就可以调用对象的几个方法来访问Anntation
信息。
- A getAnnotation(Class annotationClass):返回指定类型的注解,不存在返回
null
- A getDeclaredAnnotations(Class annotationClass):获取直接修饰该程序元素、指定类型的
Annotation
,不存在返回null
。 - Annotation[ ] getAnnotation():返回该程序元素上存在的所有注解
- Annotation[ ] getDeclaredAnnotations():返回直接修饰该程序元素的指定
Annotation
。 - boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否有指定类型的注解。
使用第一种的自定义注解,即没有成员变量的注解,它的作用是标记。如下:
1 |
|
上面的@Testable
注解用于标记哪些方法是可以测试的,该注解可以作为JUnit
测试的补充。
Java8新增注解
重复注解
在Java8以前,同一程序元素前最多只能使用一个类型的Annotation
,如果需要使用多个相同类型的注解,则必须使用Annotation
容器。Java8以前为如下形式:
1 |
@Results
注解只包含一个名为value
、类型为Result[]
的成员变量,指定多个@Result
作为value
的属性的数组元素。改进后为:
1 |
上述即为重复注解,可以被多次使用修饰某一程序元素。开发此类重复注解需要使用@Repeatable
修饰。
新增TypeAnnotation
注解
Java8为ElementType
枚举增加了TYPE_PARAMETER
、TYPE_USE
两个枚举值,这样允许定义枚举时使用@Target(ElementType.TYPE_USE)
修饰,这种注解被称为TypeAnnotation 类型注解
,TypeAnnotation
可用在任何用到类型的地方。比如:
- 创建对象时(new)
- 类型转换时
- 实现接口时
throws
声明抛出异常时
以上情况都会用到类型,因此可以使用注解来修饰。
1 |
|
要想让这些TypeAnnotation
起作用,开发者需要自己实现Type Annotation
检查框架或者使用第三方框架。
编译时处理注解
APT(Annotation Processing Tool)
是一种注解处理工具,它对源代码进行检查,并找出源文件所包含的Annotation
信息,做额外处理。使用APT
的目的是简化开发者的工作量,因为APT可以在编译程序源代码的同时生成一些附属文件,比如源文件、类文件、XML文件等,内容与源代码文件相关。简而言之,APT可以替代对代码信息和附属文件的维护工作。
Java.exe
工具有一个-processor
选项,可指定一个Annotation
处理器,如果指定了这个处理器,那么这个处理器会在编译时提取并处理源文件中的注解。每个注解处理器都需要实现javax.anntation.processing
包下的Processor
接口。不过实现该接口必须实现所有方法,因此会采用继承AbstractProcessor
方式来实现注解处理器。一个处理器可以处理一种或多种Annotation
类型!
1 | javac -processor HibernateAnnotationProcessor Person.java |
XML
文件根据源代码文件中的Annotation
生成,这是使用APT的结果!