关于注解应该知道的

什么是注解

其实注解并不复杂,使用注解一定会有三个过程:定义注解,使用注解,读取注解。我们一步一步来看:

定义注解

定义没什么好说的

1
2
3
4
5
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Six{
String name() default "nanwei";
}

  1. 使用@interface定义注解
  2. @Target@Retention是元注解,就是注解的注解。。。。。,一共四种
    1. Target表示注解的作用目标,看代码
1
2
3
4
5
6
7
8
9
10
11
12
@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
*/
ElementType[] value();
}

ElementType是一个枚举类,包括type,field,method等常见的注解目标

  1. Retention定义注解的保留策略

    1
    2
    3
    @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
    @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
    @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到,正常来说定义在运行时才能发挥大作用
  2. Document:说明该注解将被包含在javadoc中,生成java文档的时候会用到

  3. Inherited:说明子类可以继承父类中的该注解,这里要注意的是,仅类的注解可以被集成,接口的不行,类内的变量和方法也不行
    1. 定义值,关键字default表默认值,仅支持基本类型和String,不支持Object,因为注解参数是一种编译时常量,那时候还没有对象呢,这算是java的一个设计缺陷吧,不知道以后会不会改进
使用注解

使用注解就很简单了,Override什么的我们也经常用,上面我们自己定义的也一样用

1
2
3
4
5
6
7
8
public class Student implements Algorithm{
@Six(name="666")
public String name;

public void setName(String name){
this.name = name;
}
}

我们看到name这个类变量有一个Six注解,参数666,好了,我们新建一个Student类,打印它的名字:

1
2
Student student = new Student();
Utils.printString("=======>>>>1: " + student.name);

结果

1
=======>>>>1: null

好像这个注解并没有起什么作用,这是因为,这个注解虽然被编译进了class文件,加载类之后也进入了运行时,但是因为我们没有做解析这个注解的操作,所以没有生效,那么怎么解析呢?使用反射,这就是解析注解这一步

解析注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SixProcessor {
public static Student process(Student student) {
Field[] fields = student.getClass().getFields();
for (int i = 0; i < fields.length; i++) {
Six six = fields[i].getAnnotation(Six.class);
if (six != null){
try {
student.getClass().getMethod("setName", String.class).invoke(student, six.name());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
return student;
}
}

可以看到,上面对Student对象的所有类变量遍历,检查是否有Six注解,如果有,则把注解的参数值赋给Student的name:

1
2
SixProcessor.process(student);
Utils.printString("=======>>>>2: " + student.name);

结果是

1
=======>>>>2: 666

注解有什么作用呢

基于上述注解的特性,注解的主要作用有:

  1. 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
  2. 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
  3. 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】