10、注解

1、注解简介

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

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

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

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

2、注解的作用

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 开始支持,标识某注解可以在同一个声明上使用多次。

3、定义注解

【格式】public @interface 注解名称 {}

【步骤】

  1. 创建注解;
  2. 定义注解的参数和默认值;
  3. 用上元注解配置该注解。

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】使用@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

【示例2】使用@Repeatable注解获取学生所有的职位

// 注解1
// 运行期间有效
@Retention(RetentionPolicy.RUNTIME)
// 作用与类的属性上
@Target(ElementType.FIELD)
public @interface Roles {
    Role[] value();
}
// 注解2
// 运行期有效
@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("王五");
        // 判断position属性上@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());
            }
        }
    }
}

【运行】

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

10、注解

上一篇:testng---异常测试/依赖测试


下一篇:45、什么情况下会调用拷贝构造函数