注解是放在类、方法、属性、参数前的一种特殊注释,与常规注释不同,注解不会被编译器忽略,而被编译器打包进入.class
文件,注解是一种用作标注的元数据。
注解本质是一个继承了Annotation
接口的接口。
一、分类
Java 注解可分为三类:
- 由编译器使用的注解,这类注解不会被编译进
.class
文件,编译完成后就被丢掉了,如:@Override
:编译器用作检查方法是否正确地重写@Deprecated
:标记当前的类/方法/属性不再推荐被使用,可能在未来的JDK版本中被移出@SuppressWarning
:让编译器忽略此处代码产生的警告
- 由工具处理
.class
文件时使用,如一些工具在加载.class
文件时会做动态修改以实现一些特殊功能,这类注解会被编译进.class
文件,但加载结束后并不会存在于内存中,这类注解只被一些底层库使用。 - 程序运行期可以读取的注解,加载后一直存在于JVM中,这也是最常用的注解,如
@PostConstruct
会在调用构造方法后自动被调用。
二、元注解
元注解是用于修饰注解的注解,通常用在注解的定义上。 Java 中有以下几个元注解:
@Target
:注解的作用目标@Retention
:注解的生命周期@Documented
:注解是否应该被包含在 JavaDoc 文档中@Inherited
:是否允许子类继承该注解@Repeatable
:允许注解重复使用
1. @Target
@Target
用于指明被修饰的注解最终作用的目标范围,如:@Target(value={ElementType.FIELD})
,ElementType
是一个枚举类型,枚举值包括:
ElementType.TYPE
:允许被修饰的注解作用在类、接口和枚举上ElementType.FIELD
:允许被修饰的注解作用在属性字段上ElementType.METHOD
:允许被修饰的注解作用在方法上ElementType.PARAMETER
:允许被修饰的注解作用在方法参数上ElementType.CONSTRUCTOR
:允许被修饰的注解作用在构造方法上ElementType.LOCAL_VARIABLE
:允许被修饰的注解作用在本地局部变量上ElementType.ANNOTATION_TYPE
:允许被修饰的注解作用在注解上ElementType.PACKAGE
:允许被修饰的注解作用在包上
2. @Retention
@Retention
用于指明当前注解的生命周期,有一个value
属性,如:@Retention(value=RetentionPolicy.RUNTIME)
,RetentionPolicy
是一个枚举类型,枚举值包括:
RetentionPolicy.SOURCE
:当前注解编译器可见,不会写入.class
文件RetentionPolicy.CLASS
:类加载阶段丢弃,会写入.class
文件RetentionPolicy.RUNTIME
:永久保存,可以反射获取
3. @Documented
@Documented
修饰的注解在执行 JavaDoc 文档打包时会被保存进 Doc 文档。
4. @Inherited
@Inherited
修饰的注解具有可继承性,使用该注解修饰的类,其子类将自动继承父类的该注解。
5. @Repeatable
Java8 为了解决同一个注解不能重复在同一个类/方法/属性上使用的问题。
三、自定义注解
1. 可用的元素类型:
- 所有基本类型:
int
、float
、boolean
String
Class
enum
Annotation
- 以上类型的数组
2. 默认值限制
- 元素不能有不确定的值,要么有默认值,要么在使用时必须赋值
- 对于非基本类型的元素,不能以
null
作为它的值
3. 关于value()
当注解中有value
元素时,当使用注解时赋值没有指定任何元素,则默认赋值给value
。
四、示例
1. 定义一个标注范围的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 3;
}
2. 使用注解
public class Student {
@Range(min = 2, max = 4)
public String name;
@Range(min = 1, max = 2)
public int age;
}
3. 使注解生效
import java.lang.reflect.Field;
public class RangeParse {
public static void rangeParse(Student student) {
Field[] fields = student.getClass().getDeclaredFields();
for (Field field : fields) {
Range range = field.getDeclaredAnnotation(Range.class);
System.out.println("Field[" + field.getName() + "] max: " + range.max() + ", min: " + range.min());
}
}
public static void main(String[] args) {
Student student = new Student();
rangeParse(student);
}
}
运行结果
Field[name] max: 4, min: 2
Field[age] max: 2, min: 1