一、注解介绍
1. 什么是注解
- 注解也叫元数据
- 注解是JDK1.5版本开始引进的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解
2. 注解分类
-
JAVA自带的标准注解
包括@Override(标明重写某个方法)、@Deprecated(标明某个类或方法过时)和@SuppressWarnings(标明要忽略的警告),使用这些注解后编译器就会进行检查 -
元注解,元注解是用于定义注解的注解
包括@Retention(标明注解被保留的阶段)、@Target(标明注解使用的范围)、@Inherited(标明注解可继承)、@Documented(标明是否生成javadoc文档)、@Repeatable(标明注解是否可重复) -
自定义注解,根据自己的需求定义
3.注解作用
(1)生成文档,通过代码里标识的元数据生成javadoc文档
(2)编译检查,通过代码里标识的元数据,让编译器在编译期间进行检查验证
(3)编译时动态处理,编译时通过代码里标识的元数据进行动态处理,例如动态生成代码
(4)运行时动态处理,运行时通过代码里标识的元数据进行动态处理,例如使用反射注入实例
4. 注解是给谁用的
- APT
当开发者使用了注解修饰了类、方法、Field等成员之后,这些注解并不会自己生效,必须由开发者提供的相应代码来提取并且处理注解中的信息(动态代码生成)。这些处理和提取注解的代码统称为APT(Annotation Processing Tool) - 编译器
编译器在编译期间检查验证
二、元注解解析
1. @Retention
保留期,解释说明了这个注解的存活时间,取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们
2. @Target
目标,指定了注解运用的地方
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
3. @Inherited
继承,如果一个类被@Inherited注释,那么他的子类(如果没有被任何注解应用)会继承父类的注解
// 首先定义一个可被继承的注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {
}
// 父类:使用上述注解进行注释
@Test
public class A {}
// 子类:继承了父类的注解
public class B extends A {}
4. @Repeatable
可重复,JDK1.8之后加入;如果一个注解的值可同时取多个,那么这个注解将是可重复的;@Repeatable后面括号中的类相当于一个容器注解!
// Persons容器:存放Person的多个值
@interface Persons {
Person[] value();
}
// Person注解定义
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
// 一个注解的值可以同时取多个
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
注意:注解容器解释
- 注解容器中必须有一个value属性
- value属性的类型是一个被@Repeatable注解过的数组
三、反射提取注解
程序在运行期间可以通过反射获取注解对象,已经通过反射获取了某个类的被注解对象后(Annotationed Element),就可以调用以下方法访问注解信息
1.具体方法
- T getAnnotation(Class annotationClass):
返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。 - Annotation[] getAnnotations():
返回该程序元素上存在的所有注解。 - boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。 - Annotation[] getDeclaredAnnotations():
返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响
2. 样例代码:
- 首先自定义4个作用域不同的注解
/*
作用域:类、接口、枚举类上
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation_Type {
String value() default "类上注解";
}
/*
作用域:方法上
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation_Method {
String value() default "方法上注解";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation_Parameter {
String value() default "参数上注解";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation_Field {
String value() default "字段上注解";
}
- 进行测试,通过反射获取注解中的值
@MyAnnotation_Type
public class Annotation_Test {
@MyAnnotation_Field()
String field = "一个字段";
@MyAnnotation_Method
public void test(@MyAnnotation_Parameter String args) {
System.out.println("参数值 = " + args);
}
// 根据反射获取类中的注解
public static void main(String[] args) {
// 1. 获取类上的注解
MyAnnotation_Type t1 = Annotation_Test.class.getAnnotation(MyAnnotation_Type.class);
System.out.println("类上注解值 --> " + t1.value());
// 2. 获取方法上的注解
try {
Method method = Annotation_Test.class.getDeclaredMethod("test", String.class); // 首先获取方法
MyAnnotation_Method t2 = method.getAnnotation(MyAnnotation_Method.class); // 获取注解
System.out.println("方法上的注解 --> " + t2.value());
// 3. 获取方法上的所有注解
Annotation[][] annotations = method.getParameterAnnotations();
for (Annotation[] tt : annotations) {
for (Annotation t3 : tt) {
if (t3 instanceof MyAnnotation_Parameter) {
System.out.println("参数上的注解 --> " + ((MyAnnotation_Parameter) t3).value());
}
}
}
method.invoke(new Annotation_Test(), "改变默认参数");
// 获取Annotation_Test类上字段field的注解
MyAnnotation_Field t4 = Annotation_Test.class.getDeclaredField("field").getAnnotation(MyAnnotation_Field.class);
System.out.println("字段上注解 --> " + t4.value());
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 输出结果
类上注解值 --> 类上注解
方法上的注解 --> 方法上注解
参数上的注解 --> 参数上注解
参数值 = 改变默认参数
字段上注解 --> 字段上注解