注解【了解】

什么是注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

Java 注解是在 JDK5 时引入的新特性,注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。注解类型定义指定了一种新的类型,一种特殊的接口类型。 在关键词 interface 前添加 @ 符号也就是用 @interface 来区分注解的定义和普通的接口声明。目前大部分框架(如 Spring Boot 等)都通过使用注解简化了代码并提高的编码效率。

JDK 中内置的注解

Java 定义了一套注解,其中

java.lang 包下

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告
  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口

java.lang.annotation 包下

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在运行时可以通过反射访问
  • @Documented - 标记这些注解是否包含在用户文档中
  • @Target - 标记这个注解应该是哪种 Java 成员
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个地方使用相同的注解

注解的本质

我们来看一个 JDK 内置的注解: @Override 定义

1
2
3
4
5
6
7
8
package java.lang;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这个注解的本质就是一个继承了 Annotation 接口的接口

1
2
3
public interface Override extends Annotation{

}

元注解

在 JDK 1.5 中提供了 4 个标准的用来对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解), 这四个注解分别是

  • @Documented – 注解是否将包含在 JavaDoc 中
  • @Retention – 什么时候使用该注解
  • @Target – 注解用于什么地方
  • @Inherited – 是否允许子类继承该注解

@Retention

@Reteniton:指明当前注解的生命周期(即:用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时) 。

一共有三种策略,定义在 RetentionPolicy 枚举中。

1
2
3
4
5
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}

● SOURCE:在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override 就属于这类注解。
● CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
● RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式

@Target

@Target:描述注解的使用范围,默认值为任何元素。

它的取值范围定义在 ElementType 枚举中,可用的 ElementType 参数包括

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public enum ElementType {

// 描述类、接口(包括注解类型) 或enum声明
TYPE,

// 成员变量、对象、属性(枚举常量)
FIELD,

// 成员方法
METHOD,

// 方法参数
PARAMETER,

// 构造方法
CONSTRUCTOR,

// 局部变量
LOCAL_VARIABLE,

// 注解类
ANNOTATION_TYPE,

// 可用于修饰:包
PACKAGE,

// 类型参数,JDK 1.8 新增
TYPE_PARAMETER,

// 使用类型的任何地方,JDK 1.8 新增
TYPE_USE

}

@Documented

@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档。

@Inherited

@Inherited:使被它修饰的注解具有继承性(也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解)。

自定义注解

格式

public @interface 注解名 {定义体}

  • 权限修饰符只能为 public 或者默认缺省(default)
  • 如果只有一个参数成员,最好把参数名称设为”value”,后加小括号
  • 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,比如 String value() default "";

测试

自定义品牌注解,并设置默认值

1
2
3
4
5
6
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface brand {

String value() default "HUAWEI";
}

定义一个实体类,这里用了 lombok

1
2
3
4
5
6
@Data
public class Phone {

@brand
private String phone;
}

查看效果

1
2
3
4
5
6
7
8
9
10
public class Demo {
public static void main(String[] args) {
Class<Phone> phoneClass = Phone.class;
Field[] fields = phoneClass.getDeclaredFields();
for (Field field : fields) {
brand annotation = field.getAnnotation(brand.class);
System.out.println(field.getName() + ":" + annotation.value());
}
}
}