1. 注解(Annotation)概述
- 从JDK5.0开始,Java增加了对元数据
MeteDate
的支持,也就是Annotation
Annotation
其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation
,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。Annotation
可以像修饰符一样被使用,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation
的name=value
中- 在
JavaSE
中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android
中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE
旧版中所遗留的繁冗代码和XML配置等。 - 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.X以后也是基于注解的。注解是一种趋势,一定程度上可以说:框架 = 注解 + 反射 + 设计模式
2. 常见注解的示例
2.1 生成文档相关的注解
@author
:标明开发该模块的作者,多个作者之间可用,
分隔@version
:标明该模块的版本@see
:参考转向,也就是相关主题@since
:从哪个版本开始增加的@param
:对方法中某参数的说明,如果没有参数就不能写,格式:@param 形参名 形参类型 形参说明
@return
:对方法返回值的说明,如果方法返回值是void
则不写,格式:@return 返回值类型 返回值说明
@exception
:对方法可能抛出的异常进行说明,如果方法没有用throw
显式抛出异常就不能写,格式:@exception 异常类型 异常说明
其中,@param
、@return
、@exception
这三个标记都是只用于方法的,且@param
和@exception
可以并列多个
2.2 编译时格式检查
@Override
:限定重写父类的方法,该注解只能用于方法@Deprecated
:用于表示所修饰的元素已过时。通常是因为所修饰的结构危险或存在更好地选择@SuppressWarnings
:抑制编译器警告
2.3 跟踪代码依赖性,实现替换配置文件的功能
- Servlet3.0提供了注解,使得不再需要在
web.xml
文件中进行Servlet
的部署。 - spring框架中关于“事务”的管理
- 单元测试使用注解标识测试方法
2.4 JDK内置三个注解的使用
2.4.1 @Override
class Person {
private String name;
public void walk() {
}
}
class Student extends Person {
// 重写方法
@Override
public void walk() {
super.walk();
}
}
在编译时编译器会校验添加了@Override
注解的方法是否是重写,避免编码时的拼写错误
2.4.2 @Deprecated
public class AnnotationTest {
public static void main(String[] args) {
Person p = new Person();
p.walk();
}
}
class Person {
private String name;
// 标注过时方法
@Deprecated
public void walk() {
}
}
标注过时后,不代表该方法不能使用,只是给出一个提示,提示开发者该方法有新的取代方式或者是危险的
在IDE中,使用了过时的方法往往会自动加上中划线
2.4.3 @SuppressWarnings
@SuppressWarnings("unused")
public class AnnotationTest {
public static void main(String[] args) {
Person p = new Person();
p.walk();
}
}
使用该注解并指定忽略那种类型的警告后,编译器则不会提示警告信息
3. 自定义注解
- 注解声明为
@interface
- 自定义注解自动继承了
java.lang.annotation.Annotation
接口 Annotation
的成员变量在Annotation
中定义以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型。我们称其为配置参数。类型只能是八种基本数据类型、String
、Class
、enum
、Annotation
类型以及以上类型的数组。- 可以在定义
Annotation
的成员变量时为其指定初始值,指定成员变量的初始值可以使用default
关键字 - 如果只有一个参数成员,建议使用参数名为
value
- 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是
参数名=参数值
,如果只有一个参数成员,且名称为value
,可以省略value=
- 没有成员定义的
Annotation
称为标记;包含成员变量的Annotation
称为元数据Annotation
注意:自定义注解必须配上注解的信息处理流程(使用反射)才有意义
public @interface MyAnnotation {
String value() default "hello";
}
4. Java中4个元注解的使用
- JDK的元
Annotation
用于修饰其它Annotation
定义 JDK5提供了4个标准的
meta-annotation
类型,分别是Retention
:只能用于修饰一个Annotation
定义,用于指定该Annotation
的生命周期,@Retention
包含一个RetentionPolicy
类型的成员变量,使用Retention
时必须为value
成员变量指定值:RetentionPolicy.SOURCE
:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注解RetentionPolicy.CLASS
:在class
文件中有效(即class保留),当运行Java程序时,JVM不会保留注释,这一策略是默认值RetentionPolicy
.RUNTIME:在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注释,程序可以通过反射获取该注释
Target
:用于修饰Annotation
定义,用于指定被修饰的Annotation
能用于修饰哪些元素。@Target
也包含一个名为value
的成员变量,默认为任意元素,取值有:CONSTRUCTOR
:用于描述构造器FIELD
:用于描述域LOCAL_VARIABLE
:用于描述局部变量METHOD
:用于描述方法PACKAGE
:用于描述包PARAMETER
:用于描述参数TYPE
:用于描述类、接口(包括注解类型)或enum
声明
Documented
:用于指定被该元Annotation
修饰的Annotation
类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。- 定义为
Documented
的注解必须设置Retention
值为RUNTIME
- 定义为
Inherited
:被它修饰的Annotation
将具有继承性。如果某个类型使用了被@Inherited
修饰的Annotation
,则其子类将自动具有该注解- 比如:如果把标有
Inherited
注解的自定义注解标注在类级别上,子类则可以继承父类级别的注解 - 实际应用中使用较少
- 比如:如果把标有
自定义注解通常都会指明Retention
和Target
两个注解
5. JDK8中注解新特性
5.1 可重复注解
之前的写法是定义一个新的注解,其中value
的类型时一个注解数组,通过此方法在使用时即可实现注解的重复使用:
public @interface MyAnnotation {
String value() default "hello";
}
public @interface MyAnnotations {
MyAnnotation[] value();
}
@MyAnnotations({@MyAnnotation, @MyAnnotation(value = "test")})
class Person {
private String name;
public void walk() {
}
}
JDK8新特性的使用方式:
- 在
MyAnnotation
上声明@Repeatable
,成员值为MyAnnotations.class
MyAnnotation
的@Target
和@Retention
必须和MyAnnotations
的相同
5.2 类型注解
- 在JDK8之后,关于元注解
@Target
的参数类型ElementType
枚举值新增了两个:TYPE_PARAMETER
,TYPE_USE
在Java8之前,注解只能是在声明的地方所用,Java8开始,注解可以应用在任何地方
ElementType.TYPE_PARAMETER
表示该注解能写在类型变量声明的语句中(如:泛型声明)ElementType.TYPE_USE
表示该注解能写在使用类型的任何语句中
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface MyAnnotation {
String value() default "hello";
}
class Generic<@MyAnnotation T> {
public void show() throws @MyAnnotation RuntimeException{
ArrayList<@MyAnnotation String> list = new ArrayList<>();
int num = (@MyAnnotation int)10L;
}
}