Java(三)注解

136 阅读3分钟

注解是放在类、方法、属性、参数前的一种特殊注释,与常规注释不同,注解不会被编译器忽略,而被编译器打包进入.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. 可用的元素类型:

  • 所有基本类型:intfloatboolean
  • 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