一、什么是注解
注解(Annotation)即代码里的特殊标记,JDK5.0 之后引入了 Annotation 的概念来描述元数据,在 Java 中元数据以标签的形式存在于 Java 代码中,但是元数据标签的存在并不影响程序代码的编译和执行
在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能(@Deprecated)、限定必须重写父类的方法(@Override)、抑制编译器警告(@SuppressWarning)等.但是在JavaEE 中注解占据了更加重要的角色,例如之前需要实现 AOP 时,需要繁冗的 XML 配置,有了注解之后实现 AOP 变得更加的简洁.注解是一种趋势,在一定程度上可以说 框架 = 注解 + 反射 + 设计模式
二、常见的注解示例
1、文档注释相关注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
// @Retention 的取值
RetentionPolicy value();
}
@Rentention 的取值选项如下
@Rentention(RententionPolicy.SOURCE / RententionPolicy.CLASS / RententionPolicy.RUNTIME)
RententionPolicy.SOURCE: 注解只在源码阶段保留,编译器编译的时候将会被丢弃
RententionPolicy.CLASS: 注解只被保留至编译器编译后的字节码文件,它不会被虚拟机加载,如果没有注明使用其它的值,默认的取值就是 RententionPolicy.CLASS
RententionPolicy.RUNTIME: 注解能够保留至运行期,通过反射可以获取该注解的信息
3.2、@Target
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */
// @Target 的取值 ElementType[] value(); }
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */
// 类、接口(包括注解类型)、或者是枚举声明上 TYPE,
/** Field declaration (includes enum constants) */
// 字段上 FIELD, /** Method declaration */
// 方法上 METHOD, /** Formal parameter declaration */
// 参数上 PARAMETER, /** Constructor declaration */
// 构造方法上 CONSTRUCTOR, /** Local variable declaration */
// 局部变量上 LOCAL_VARIABLE, /** Annotation type declaration */
// 注解上 ANNOTATION_TYPE, /** Package declaration */
// 包上 PACKAGE, /** * Type parameter declaration * * @since 1.8 */
// 参数类型声明上 TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */
TYPE_USE }
3.3、@Documented
如果某一个注解上面使用了 @Documented 注解,那么该注解将会被 JavaDoc 工具提取成文档,定义了 @Documented 的注解必须包含@Rentention(RetentionPolicy.RUNTIME)
3.4、@Inherited
// 自定义 @TestInheritedAnnotation 注解,然后在该注解上使用元注解 @Inherited
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestInheritedAnnotation {
String value();
}
// 定义动物类作为父类,并且使用了 @TestInheritedAnnotation 注解
@TestInheritedAnnotation(value = "xiaomaomao")
public class Animal {
}
// 定义 Animal 的子类 Cow,测试 Cow 中是不是也有 @TestInheritedAnnotation 注解
public class Cow extends Animal {
public static void main(String[] args) {
System.out.println(isHasInheritedAnnotation());
}
public static String isHasInheritedAnnotation() {
String value = "";
// 判断 Cow 类上是否使用了 @TestInheritedAnnotation 注解
boolean hasAnnotation = Cow.class.isAnnotationPresent(TestInheritedAnnotation.class);
if (hasAnnotation) {
TestInheritedAnnotation annotation = Cow.class.getAnnotation(TestInheritedAnnotation.class);
value = annotation.value();
}
return value;
}
}
3.5、@Repeatable
可重复注解, @Repeatable 注解是 JDK 8 之后才加入进来的,是 JDK 8 新特性,它的意思是注解的值可以取多个
// 自定义容器注解 Person
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Person {
// @Person 注解是 @RepeatableAnnotation 的容器,定义类型为注解数组
RepeatableAnnotation[] value();
}
// 自定义注解 RepeatableAnnotation
// 使用 @Repeatable 注解的时候,必须要为当前注解定义一个容器注解,我们这里是 @Person
@Repeatable(Person.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RepeatableAnnotation {
String role() default "coder";
}
// 测试类
@RepeatableAnnotation(role = "president")
@RepeatableAnnotation(role = "product manager")
@RepeatableAnnotation(role = "technology manager")
@RepeatableAnnotation(role = "cleaner")
public class RepeatableAnnotationDemo {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
// 判断 RepeatableAnnotationDemo 类上是否使用了@Person 注解
boolean hasPersonAnnotation = RepeatableAnnotationDemo.class.isAnnotationPresent(Person.class);
if (hasPersonAnnotation) {
// 获取 RepeatableAnnotationDemo 类上 Person 注解
Person personAnnotation = RepeatableAnnotationDemo.class.getAnnotation(Person.class);
// 通过 Person 注解容器获取到该容器内的所有 @RepeatableAnnotation 注解
RepeatableAnnotation[] repeatableAnnotations = personAnnotation.value();
// 获取所有的 @RepeatableAnnotation 中的 role 值并拼接输出
for (RepeatableAnnotation repeatableAnnotation : repeatableAnnotations) {
stringBuffer.append(repeatableAnnotation.role() + "===>");
}
System.out.println(stringBuffer);
}
}
}
// 测试结果
president===>product manager===>technology manager===>cleaner===>
三、注解的属性
注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量是以 无形式参数的方法 来声明的,其方法中定义了该成员变量的类型和成员方法的名字
注意: 在注解中定义属性,属性的类型必须是八种基本数据类型、类、接口、注解以及它们的数组
如下自定义的注解中有 int 类型的 id,它有一个默认值 10086、String 类型的 userName、以及 String 数组类型的 hobby
@Retention(value=RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) public @interface TestAnnotation {
// 注解中可以有默认值,使用 default 关键字来指定 int id() default 10086; String userName(); String[] hobby(); }
1、如何给注解赋值
// 根据 @TestAnnotation 的属性类型和属性名称依次给注解赋值,多个值用逗号隔开,由于 id 属性有默认值,
// 那么这里可以不用给id赋值,如果显示的给id赋值的话,那么它会覆盖默认值
@TestAnnotation(userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
}
2、如果注解只有一个属性的情况
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
// 如果注解只有一个属性,请使用 value 来命名该属性
String value();
}
取值的时候需要注意
// 如果注解只有一个属性可以不写 value,直接 @TestAnnotation("xiaomaomao") 赋值也是一样的
@TestAnnotation(value="xiaomaomao")
public class AnnotationDemo {
}
四、自定义注解
1、定义新的 Annotation 类型使用 @interface 关键字.
2、自定义注解自动继承了java.lang.annotation.Annotation 接口.
3、Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数.类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组.
4、可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字.
5、如果只有一个参数成员,建议使用参数名为 value.
6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值.格式是 "参数名 = 参数值",如果只有一个参数成员,且名称为 value,可以省略 "value="
7、没有成员定义的 Annotation 称为标记;包含成员变量的 Annotation 称为元数据 Annotation.
特别注意:自定义注解必须配上注解的信息处理流程才有意义.
// 自定义注解 TestAnnotation
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
int id() default 10086;
String userName();
String[] hobby();
}
// 测试类
@TestAnnotation(id=10001,userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
public static void main(String[] args) {
// 判断 AnnotationDemo 类上是否使用了 @TestAnnotation 注解
boolean hasAnnotation = AnnotationDemo.class.isAnnotationPresent(TestAnnotation.class);
StringBuffer stringBuffer = new StringBuffer();
if (hasAnnotation) {
// 获取 AnnotationDemo 上的所有注解,这里只有一个 @TestAnnotation 注解
Annotation[] annotations = AnnotationDemo.class.getAnnotations();
for (Annotation annotation : annotations) {
// 获取 @TestAnnotation 注解中的属性并进行拼接
if (TestAnnotation.class.equals(annotation.annotationType())) {
TestAnnotation testAnnotation = (TestAnnotation) annotation;
int id = testAnnotation.id();
String userName = testAnnotation.userName();
String[] hobbys = testAnnotation.hobby();
stringBuffer.append(id);
stringBuffer.append("
");
stringBuffer.append(userName);
stringBuffer.append("
");
stringBuffer.append("[");
for (String hobby : hobbys) {
stringBuffer.append(hobby+"、");
}
stringBuffer.append("]");
stringBuffer.append("
");
// 获取 @TestAnnotation 上的注解类型并进行拼接
Annotation[] underTestAnnotations = testAnnotation.annotationType().getAnnotations();
for (Annotation underTestAnnotation : underTestAnnotations) {
Class<? extends Annotation> aClass = underTestAnnotation.annotationType();
stringBuffer.append(aClass);
stringBuffer.append("
");
}
}
System.out.println(stringBuffer);
}
}
}
}
// 测试结果
10001
xiaomaomao
[film、novel、play computer games、]
interface java.lang.annotation.Retention
interface java.lang.annotation.Target