1. 注解的定义
注解也是一种引用类型,编译后会生成 .class 字节码文件,作用就是为程序进行标识,不同注解能实现不同功能。
2. 注解的使用
3. 注解的读取执行
3.1 得不到注解信息,得到的是null
3.2 在MyAnnotation1注解 加上 @Retention(RetentionPolicy.RUNTIME) 以及 MyAnnotation3注解 加上 @Retention(RetentionPolicy.CLASS)后,得到了MyAnnotation1注解信息,但没得到MyAnnotation3注解信息,如下图。
4. java中的4个元注解:@Documented、@Inherited、@Retention和@Target
4.1 @Documented注解使用后会被javadoc工具处理,内部没有属性,@Documented注解源代码如下图。
4.2 @Inherited注解:包含@Inherited的注解会被子类继承;@Inherited注解只能在类使用,不能用于方法、属性等;查询某个类是否使用了包含@Inherited的注解时,不仅会查询当前类,还会在父类中查询,直至到达Object类。@Inherited注解源代码及使用如下图。
4.3 @Target注解:指定注解被用于什么位置,是用在类上还是方法上或是属性等等,由ElementType数组指定,可指定的位置主要有10种,@Target注解源代码及ElementType取值范围及@Target注解使用如下图所示。
4.4 @Retention注解:表示注解保存到什么时候。@Retention注解源代码及RetentionPolicy取值范围如下图所示,@Retention注解的使用在3.2中已经分析过。
5. 深入了解注解的本质及动态获取注解属性值
5.1 我们以MyAnnotation2和MyAnnotation3注解为例,代码如下。
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
int age();
String name();
int[] hobby();
}
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}
5.2 然后使用注解的测试类AnnotationTest代码如下。
5.3 读取注解并执行,代码如下。
5.4 到这里,我们是不是算完成注解的定义、使用及读取了呢?其实这边还有两个问题:1.为什么注解中的属性获取和执行方法形式一致?2.我们可不可以动态、自动地获取注解的所有属性及属性值呢?
5.5 我们先加载注解MyAnnotation2,并查看一下注解的信息,如下图。
同时查看一下字节码的反编译代码,虽然注解的形式和接口几乎一致,但是有一个操作我们很费解,就是可以给注解里的属性赋值,我们刚说完注解的属性实际是方法,那么方法怎么能赋值呢?所以这里有个奇怪的点,所以我们还是要特别对待注解和接口。
5.6 既然注解中的属性实际上是方法,那么想通过反射获取属性值,是不是也是通过方法反射,而不是属性反射呢?结果如下图。
5.7 通过5.5和5.6,我们解决了两个问题:为什么注解中的属性和方法一样的形式;怎么能够通过反射自动、动态地获取注解所有属性及属性值。
附录:代码和最终运行结果
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
@MyAnnotation3("class_annotation")
public class AnnotationTest{
@MyAnnotation3("mem_annotation")
private String mem;
public static void main(String[] args) throws Exception {
// 通过反射得到AnnotationTest类
Class<?> annotationTest = Class.forName("AnnotationTest");
// 获取AnnotationTest类使用的MyAnnotation3注解
MyAnnotation3 annotation = annotationTest.getAnnotation(MyAnnotation3.class);
System.out.println("获取类AnnotationTest使用的MyAnnotation3注解信息:" + annotation);
// 通过反射,动态获取AnnotationTest类的所有属性
// 这里要注意getDeclaredFields方法是得到当前类所有声明的方法,不管访问权限,但不包含父类的方法
// getFields方法则是得到访问权限是public的方法,包含父类的公共方法。
Field[] declaredFields = annotationTest.getDeclaredFields();
for(Field f: declaredFields){
// 获取属性使用的MyAnnotation3注解信息
MyAnnotation3 annotation1 = f.getAnnotation(MyAnnotation3.class);
if(annotation1 != null){
System.out.println("获取属性mem使用的MyAnnotation3注解的value属性值:" + annotation1.value());
}
}
// 与上面类似
Method[] declaredMethods = annotationTest.getDeclaredMethods();
Class<?> myAnnotation2 = Class.forName("MyAnnotation2");
for(Method m: declaredMethods){
MyAnnotation2 annotation1 = m.getAnnotation(MyAnnotation2.class);
if(annotation1 != null){
// 通过反射自动、动态地获取注解的属性及属性值
System.out.print("通过反射获取MyAnnotation2注解的属性及属性值, ");
Method[] declaredMethods2 = myAnnotation2.getDeclaredMethods();
for(Method mm: declaredMethods2){
if(mm.getName().equals("hobby")){
System.out.print(mm.getName() + ":" + Arrays.toString((int[])mm.invoke(annotation1)) + " ");
}else{
System.out.print(mm.getName() + ":" + mm.invoke(annotation1) + " ");
}
}
System.out.println();
}
MyAnnotation3 annotation2 = m.getAnnotation(MyAnnotation3.class);
if(annotation2 != null){
System.out.println("获取方法" + m.getName() + "使用的MyAnnotation3注解信息: " + annotation2.value());
}
}
}
@MyAnnotation3("test1")
static void test1(){
}
@MyAnnotation2(name = "lhj", age = 15, hobby = {1,3})
static void test2(){
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
int age();
String name();
int[] hobby();
}
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}