注解

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。注解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

12.注解
12.1.概念
  • 注释:用文字描述程序的。给程序员看的
  • 注解(Annotation):也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
  • 概念描述:
    • JDK1.5之后的新特性
    • 说明程序的
    • 使用注解:@注解名称
  • 注解的保留级别:注解的保留级别限定的是注解的存在状态
    • SOURCE:注解将被编译器丢弃(class文件中没有)
    • CLASS:注解在class文件中可用,但会被JVM丢弃(内存中没有)
    • RUNTIME:JVM在运行时,也会保留注释信息
12.2.JDK中预定义的一些注解
  • @Override:检测被该注解标注的方法是否是继承自父类(接口)的
  • @Deprecated:该注解标注的内容,表示已过时
  • @SuppressWarnings:压制警告,一般传递参数all @SuppressWarnings(“all”)
12.3.自定义注解
/*
 @interface 注解名 {
 
    // 第一条信息:
    // 名称: 方法名1
    // 取值类型: 返回值类型1
    返回值类型1  方法名1();
    
    // 第二条信息
    // 名称: 方法名2
    // 取值类型: 返回值类型2
    返回值类定2  方法名2();
    ...
 }
 */

//自定义注解举例
@Retention(RetentionPolicy.RUNTIME)
public @interface AgeConstraint {

    // 年龄的上界
    int maxAge();

    // 年龄的下界
    int minAge();
}

@Retention(RetentionPolicy.RUNTIME)
public @interface NameLengthConstraint {

    // 描述名字字符串的长度上界
    int maxLength();
}
  • 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
    • public interface MyAnno extends java.lang.annotation.Annotation {}
  • 属性:接口中的抽象方法
    • 注解体的格式类似于接口中的方法定义,但含义完全不同
    • 方法名就是数据的名称,方法的返回值类型,表示数据值的类型。
    • 每一条注解信息,它的取值的类型只能是一下几种:
      1. 所有的基本数据类型
      2. String类型
      3. Class类型
      4. 注解类型
      5. 以及以上类型的数组
    • 定义了属性,在使用时需要给属性赋值
      1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
      2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
      3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
      4. 对于合法的引用类型的数据, 比如String类型, 它的默认取值不能是null
12.4.注解处理器
  • 必须提供注解处理器,自定义注解才能生效
  • 方式之一:反射,下面是反射类里的常用关于注解的方法
    • public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断变量/方法到底有没有被注解,参数为被判断的变量/方法
    • public <T extends Annotation> T getAnnotation(Class<T> annotationClass):如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

    这两个方法只能由Class、Method、Field三种反射得到的对象调用。

    这两个方法位于java.lang.reflect.AnnotatedElement接口里,只不过其他类实现了它们。

  • 使用步骤
    1. 实现注解实例
    2. 给注解实例中的每一个属性(每一条信息),取具体值(赋值) 取值。
    3. 在定义好注解类型,利用注解实例,给代码添加额外信 @注解的类型名(属性名1=属性值1,属性名2=属性值2,....)
    4. 配置注解处理器
/*
	定义一个Student类,包含name和age两个成员
    	1.name中包含的字符个数不得超过指定值(具体的约束条件信息-> 注解)
        2.age必须在指定范围内(具体的约束条件信息-> 注解)
        3.name和age都满足条件才能创建Student对象,否则抛出异常。(该效果由注解处理器来实现)

    通过反射技术,实现一个自定义处理器,用来Student对象
        1.name和age取值满足约束,才创建对象
        2.否则,抛出异常,拒绝创建
 */


//注解的实例
public class Student {
    @AgeConstraint(maxAge = 25, minAge = 18)
    private int age;

    @NameLengthConstraint(maxLength = 5)
    private String name;

    private Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}


//利用反射实现注解的功能,即注解处理器
public class StudentFactory {
    // Student类对应的Class对象
    private Class studentClz;

    public StudentFactory() {
        studentClz = Student.class;
    }

    public Student getStudent(int age, String name) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        // 1.判断age初始化参数的取值,是否满足约束
        judgeAge(age);

        // 2.判断name初始化参数的取值,是否满足约束
        //    a. 不满足约束,直接抛出异常
        //    b. 否则,准备创建对象
        judgeName(name);

        // 3.创建Student对象并返回
        Constructor constructor = studentClz.getDeclaredConstructor(int.class, String.class);
        constructor.setAccessible(true);
        Student st = (Student) constructor.newInstance(age, name);

        return st;
    }

    private void judgeName(String name) throws NoSuchFieldException {
        Field nameField = studentClz.getDeclaredField("name");
        if (nameField.isAnnotationPresent(NameLengthConstraint.class)) {
            // 如果有,获取
            //System.out.println("name成员变量上,有NameLengthConstraint注解");
            NameLengthConstraint nameAnnotation =
                    nameField.getAnnotation(NameLengthConstraint.class);


            // 访问注解实例,获取约束
            int length = nameAnnotation.maxLength();

            // 处理
            if (name.length() > length) {
                // 名字长度太长
                throw new IllegalArgumentException("name 参数非法:" + name);
            }
        }
    }

    private void judgeAge(int age) throws NoSuchFieldException {
        // 因为对于age成员变量的约束信息,是定义在age成员变量上的,
        // 该信息属于age成员的定义信息。
        Field ageField = studentClz.getDeclaredField("age");

        // 从该成员变量上,获取其约束信息(即注解实例)
        // a. 先判断,该成员变量是否定义了,指定类型的注解实例
        //  Field对象.isAnnotationPresent(Class annotationType)
        //  有: 返回true
        //  没有: 返回false
        if(ageField.isAnnotationPresent(AgeConstraint.class)) {
            //System.out.println("age成员变量上,有AgeConstraint注解");
            // 如果定义了AgeConstraint类型的直接实例,获取
            //  获取指定类型的注解实例
            // Field对象上.getAnnotation(class annotationType);
            AgeConstraint ageAnnotation = ageField.getAnnotation(AgeConstraint.class);

            // 访问注解实例,获取注解实例的属性值
            //  注解实例.属性名()
            int maxAge = ageAnnotation.maxAge();
            int minAge = ageAnnotation.minAge();

            // 处理
            if (age < minAge || age > maxAge) {
                // 不满足约束,抛出异常
                throw new IllegalArgumentException("age 参数非法: " + age);
            }
        }
    }
}
12.5.元注解
  • 用于描述注解的注解
    • @Target:描述注解能够作用的位置
      • ElementType取值:
        • ElementType.TYPE:可以作用于类上
        • ElementType.METHOD:可以作用于方法上
        • ElementType.FIELD:可以作用于成员变量上
        • ElementType.CONSTRUCTOR:可以作用于构造方法上
        • ElementType.LOCAL_VARIABLE:可以作用于局部变量上
        • ElementType.PARAMETER:可以作用于方法参数前
      @Target (value ={elementType.TYPE,elementType.METHOD,element.FIELD})
      
    • @Retention:描述注解被保留的阶段
      • @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
      • RetentionPolicy.RUNTIME
      • RetentionPolicy.CLASS
      • RetentionPolicy.SOURCE
      @Target (value ={elementType.TYPE,elementType.METHOD,element.FIELD})
      
    • @Documented:描述注解是否被抽取到api文档中
    • @Inherited:描述注解是否被子类继承
  • 因此,我们需要在自定义注解前边加上 @Retention(RetentionPolicy.RUNTIME)
//定义一个测试类
public class Test {
    
    public static void main(String[] args) throws 
        InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 测试注解处理器
        // 注解处理器
        StudentFactory studentProcessor = new StudentFactory();

        // 参数满足约束
        //Student st1 = studentProcessor.getStudent(19, "张三");
        //System.out.println(st1);

        // 参数不满足约束
        Student st2 = studentProcessor.getStudent(30, "1234567");
        System.out.println(st2);
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181069.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!