目录

  • 一、注解简介
  • 二、注解作用
  • 2.1、内置注解
  • 2.2、用在代码上的注解
  • 2.3、用在其它注解上的注解
  • 三、定义注解
  • 3.1、创建注解
  • 3.2、定义参数和默认值
  • 3.3、用元注解配置注解
  • 3.3.1、@Retention
  • 3.3.2、@Target
  • 3.3.3、@Documented
  • 3.3.4、@Inherited
  • 3.3.5、@Repeatable
  • 四、处理注解
  • 4.1、示例1
  • 4.2、示例2

一、注解简介

Java 注解(Annotation)又称为 Java 标注,是 Java5 开始支持加入源代码的特殊语法元数据。

Java 语言中的类、方法、变量、参数和包等都可以被标注。

Java 标注可以通过反射获取标注的内容,在编译器生成 class 文件时,标注可以被嵌入到字节码中。

Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容。

二、注解作用

2.1、内置注解

Java 定义了一套注解,共有 10 个。5 个在 java.lang 包中,5 个在 java.lang.annotation 包中。

2.2、用在代码上的注解

注解

描述

@Override

检查该方法是否正确地重写了父类的方法。如果重写错误,会报编译错误。

@Deprecated

标记废弃的方法。如果使用该方法,会报编译警告。

@SuppressWarnings

提示编译器忽略注解中声明的警告。

@SafeVarargs

Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface

Java 8 开始支持,标识一个匿名函数或函数式接口。

2.3、用在其它注解上的注解

此类注解也称为元注解(meta annotation)。

注解

描述

@Retention

标记在什么时候保存该注解信息,用于描述注解的生命周期。

@Target

标记该注解的使用范围。

@Documented

标记这些注解是否包含在用户文档中。

@Inherited

标记子类可以继承父类的注解。

@Repeatable

Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

三、定义注解

格式:public @interface 注解名称 {}

步骤:创建注解、定义注解的参数和默认值、用上元注解配置该注解。

3.1、创建注解

示例:定义一个可用于检查字符串长度的注解

public @interface Length {
    
}

3.2、定义参数和默认值

public @interface Length {
	// 最小长度
	int min() default 0;
	// 最大长度
    int max() default Integer.MAX_VALUE;
	// 长度不合法
    String message() default "长度不合法";
}

注意:

  1. 注解的参数类似无参数方法。另外参数的类型可以是基本数据类型、String 类型、枚举类型、Class 类型、Annotation 类型以及这些类型的一维数组。
  2. 若注解中只有一个参数或这个参数是最常用的参数,应将此参数命名为 value 。在调用注解时,若参数名称是 value 且只有一个参数,则可省略参数名称。
  3. 可以使用 default 关键字来指定参数的默认值,推荐为每个参数都设定一个默认值。

3.3、用元注解配置注解

3.3.1、@Retention

Retention 译为保留,该注解定义了一个注解的生命周期。

参数

描述

RetentionPolicy.RUNTIME

(运行期间有效)注解可以保留到程序运行的时候,它会被加载进入到JVM中,所以在程序运行时可以获取到它们

RetentionPolicy.CLASS

(编译期间有效)注解只被保留到编译进行的时候,它并不会被加载到JVM中

RetentionPolicy.SOURCE

(源码期间有效)注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视

// 运行期间有效
@Retention(RetentionPolicy.RUNTIME) 
public @interface Length {
	int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不合法";
}

3.3.2、@Target

Target 译为目标,该是最为常用的元注解,可以指定自己能够被应用于源码中的哪些位置。

参数

描述

ElementType.TYPE

给一个类型进行注解,比如类、接口、枚举

ElementType.FIELD

给属性进行注解

ElementType.METHOD

给方法进行注解

ElementType.PARAMETER

给一个方法内的参数进行注解

ElementType.CONSTRUCTOR

给构造方法进行注解

ElementType.LOCAL_VARIABLE

给局部变量进行注解

ElementType.ANNOTATION_TYPE

给一个注解进行注解

ElementType.PACKAGE

给一个包进行注解

// 只用用于类属性或局部变量上
@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE})
public @interface Length {
	int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不合法";
}

3.3.3、@Documented

Documented 译为文档,标注了此注解的注解,能够将注解中的元素包含到 Javadoc 中去。

// 将该注解元素添加到JavaDoc中
@Documented
public @interface Length {
	int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不合法";
}

3.3.4、@Inherited

Inherited 译为继承,使用该注解定义子类是否可继承父类定义的注解。

@Inherited 仅针对 @Target(ElementType.TYPE) 类型的注解有效,并且仅针对类的继承有效,对接口的继承无效。

// 使用该注解的类,其子类默认继承该注解(仅针对类的继承有效)
@Inherited
// 给一个类型进行注解,类、接口、枚举等
@Target(ElementType.TYPE)
public @interface Length {
	int min() default 0;
    int max() default Integer.MAX_VALUE;
    String message() default "长度不合法";
}

此时一个类用到了该注解

@Length(min = 2, max = 16, message = "昵称长度2~16之间")
public class Pet {}

其子类默认继承了该注解

// 相当于继承了@Length
public class Cat extends Pet {}

3.3.5、@Repeatable

Repeatable 译为可重复,使用该元注解可以定义注解是否可重复。

// 作用于类的属性上
@Target(ElementType.FIELD)
// 注解Role注解,在类的属性上可使用多个Role注解
@Repeatable(Roles.class)
public @interface Role {
    String value() default "";
}
// 作用于类的属性上
@Target(ElementType.FIELD)
public @interface Roles {
    Role[] value();
}

@Repeatable 元注解标注了 @Role,而 @Repeatable 后面括号中的类相当于一个容器注解。

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解标注过的注解数组。

经过 @Repeatable 修饰后,在某个类型声明处,就可以添加多个@Role注解:

public class Student {
	@Role("学习委员")
	@Role("体育委员")
    private String name;
}

四、处理注解

4.1、示例1

需求:使用 @length 注解判断学生姓名长度是否合法。

示例:

// 运行期间有效
@Retention(RetentionPolicy.RUNTIME)
// 作用于类的属性上
@Target({ElementType.FIELD})
public @interface Length {
	// 最小长度
    int min() default 0;
    // 最大长度
    int max() default Integer.MAX_VALUE;
	// 提示信息
    String message() default "长度不合法";
}
public class Student {

    @Length(min = 1, max = 3, message = "名称超长")
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) throws NoSuchFieldException {
        Student student = new Student("上官飞燕");
        // 判断name属性上length注解是否存在
        boolean lengthPresent = student.getClass().getDeclaredField("name").isAnnotationPresent(Length.class);
        if (lengthPresent) {
            // 获取注解内容
            Length length = student.getClass().getDeclaredField("name").getAnnotation(Length.class);
            // 使用注解信息校验名称是否合法
            if (length.min() <= student.getName().length() && student.getName().length() <= length.max()) {
                System.out.println("名称合法");
            } else {
                System.out.println(length.message() + "," +
                        "合法长度:" + length.min() + "~" + length.max() + "。" +
                        "当前长度:" + student.getName().length());
            }
        } else {
            System.out.println("Student.name属性上没有找到@Length注解");
        }
    }
}

运行:

名称超长,合法长度:1~3。当前长度:4

4.2、示例2

需求:使用 @Repeatable 注解获取学生所有的职位。

示例:

// 运行期间有效
@Retention(RetentionPolicy.RUNTIME)
// 作用于类的属性上
@Target(ElementType.FIELD)
public @interface Roles {
    Role[] value();
}
// 运行期有效
@Retention(RetentionPolicy.RUNTIME)
// 作用与类的属性上
@Target({ElementType.FIELD})
// 注解Role注解,在类的属性上可使用多Role注解
@Repeatable(Roles.class)
public @interface Role {
    String value() default "";
}
public class Student {

    @Role("学习委员")
    @Role("体育委员")
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) throws NoSuchFieldException {
        Student student = new Student("王五");
        // 判断name属性上@Role注解是否存在
        Role[] roles = student.getClass().getDeclaredField("name").getAnnotationsByType(Role.class);
        if (roles.length == 0) {
            System.out.println("Student.name属性上没有找到@Role注解");
        } else {
            // 获取注解内容
            for (Role role : roles) {
                System.out.println(student.getName() + " = " + role.value());
            }
        }
    }
}

运行:

王五 = 学习委员
王五 = 体育委员