使用注解

注解的定义

注解是写在类、方法、属性上的一种注释。区别于注释,注释不会被JVM编译到 class 文件中,而注解会被带入,是一种用做标记的”元数据”。

注解用于对代码额外添加描述信息,这些信息被编译器,开发工具或运行时环境读取和处理,不会影响代码原本逻辑。

注解的作用

注解区分为三类:

  1. 类似与 @Override、@SuppressWarnings 注解用于编译环节给编译器使用,校验方法是实现的和忽略警告。编译后不回带入到 class 文件中。
  2. 由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
  3. 程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。

注解的使用

注解可以用用在类、方法、属性上,注解一般都会存在参数,参数类型为:

  • 所有的基本类型;
  • String;
  • 枚举类型;
  • 基本类型、Class以及枚举数组。 注解一般都会有默认的属性 value ,且属性都是常量的。
  • 注解属性可能存在默认值,如果不填写属性则取默认值;
  • 注解属性为常量,所以在使用时就需要给参数赋值。

代码实例

public class AnnotationDemo {  
  
    @NotNull //不能为空注解  
    private String name;  
  
    @Check(99) //value赋值为99  
    private int age;  
}

自定义注解

Java 语言使用 @interface 关键字来自定义注解,格式如下:

public @interface CustomizationAnnotationDemo {  
    String value() default "0";  
}

注解的参数一般用 value 表示,且推荐给参数加默认值。

元注解

注解的定义需要结合 JAVA 自带的一些注解,这些注解称为元注解。

@Target

用于标注注解的作用域,注解的参数可以设置可标注自定义注解用于 类或接口、方法、属性、方法参数。

  • 类或接口: ElementType.TYPE
  • 方法: ElementType.METHOD
  • 属性: ElementType.FIELD
  • 构造方法: ElementType.CONSTRUCTOR
  • 方法参数: ElementType.PARAMETER

参数类型是 ElementType[] 可以传入多个作用域,若只有一个作用范围可忽略数组写法。 @Target({ElementType.TYPE, ElementType.METHOD})@Target(ElementType.TYPE)

@Retention

此注解作用为设置自定义注解的生命周期。

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期: RetentionPolicy.RUNTIME

该注解只能传入一个参数,当不传入参数时默认作用范围为class文件。

@Repeatable

设置自定义注解是否可重复使用。

@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Hello {
}

补充: 改注解的参数需要为当前自定义注解再设置一个容器注解,示例:

// 声明被重复注解的容器注解(必须)  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@interface Filters {  
    CustomizationAnnotationDemo[] value(); // 必须定义 value() 方法,返回被重复注解的数组  
}
 
@Repeatable(Filters.class)  
public @interface CustomizationAnnotationDemo {  
    String value() default "0";  
}

@Inherited

设置自定义注解注释后的类其子类自动添加该注解。且必须为子类实现类不行。

@Inherited
@Target(ElementType.TYPE)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

在使用自定义注解后:

@Report(type=1)
public class Person {
}

其子类也拥有改注解的能力:

public class Student extends Person {
}

如何自定义注解

  1. 先使用@interface 关键字自定义注解,并且设计注解参数给定默认值。
  2. 用以上元注解设置作用范围及生命周期等。
@Target({ElementType.TYPE, ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.PARAMETER})  
@Retention(RetentionPolicy.RUNTIME)  
@Inherited  
public @interface CustomizationAnnotationDemo {  
    String value() default "0";  
}

处理注解

@Target(ElementType.FIELD)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Range {  
    int min() default 0;  
    int max() default 255;  
}
 
 
@Data  
public class UserDemo {  
    @Range(min = 0,max = 20)  
    public String name;  
    @Range(min = 1,max = 3)  
    public int age;  
    public String sex;
}
 
//处理注解限制字段长度。
public class AnnotationDemo {  
    public static void main(String[] args) throws IllegalAccessException {  
        UserDemo userDemo = new UserDemo("张三", 181, "男");  
        AnnotationDemo annotationDemo = new AnnotationDemo();  
        annotationDemo.check(userDemo);  
    }  
  
    public void check(UserDemo userDemo) throws IllegalAccessException {  
        Field[] fields = userDemo.getClass().getFields();  
        for (Field field : fields) {  
            Range range = field.getAnnotation(Range.class);  
            if (range != null) {  
                int min = range.min();  
                int max = range.max();  
                Object object = field.get(userDemo);  
                try {  
                    if (object instanceof String){  
                        if (((String) object).length() < min || ((String) object).length() > max) {  
                            throw new RuntimeException("字段" + field.getName() + "长度超出范围");  
                        }  
                    }  
                    if (object instanceof Integer){  
                        int value = (int) field.get(userDemo);  
                        if (value < min || value > max) {  
                            throw new RuntimeException("字段" + field.getName() + "大小超出范围");  
                        }  
                    }  
                } catch (IllegalAccessException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}