DAY29 注解

注解

  1. 注解的定义
  2. 注解的使用
  3. 注解的处理

一、注解的定义

public @interface 注解名{
		定义体
}
  1. 注解的定义和类的定义以及接口很像
  2. 就像类,接口在JAVA中地位一样,他也代表数据类型,定义的一个注解就是一种数据类型,一个注解数据类型定义了:具体包含多少条,具体的信息,以及每种信息是怎样的
  3. @必不可少
  4. 注解之间不能继承
  5. 在定义中,一条信息,合法的取值有:
    1. 所有的基本数据类型
    2. String
    3. Class类型
    4. 以上类型的数组
@interface AgeConstraint {
  // 定义年龄取值的下界
  int lowerBound() default 18;
  //定义年龄取值的上界
  int upperBound();
}

public class Stu {
  // 年龄的取值范围是:18-25
  // 使用注解实例,给代码,添加额外信息
  @AgeConstraint(upperBound = 25)
  public int age;
}

二、注解的使用:在定义好注解类型,利用注解实例给代码添加额外信息
@注解的类型名(属性名1=属性值1,属性名2=属性值2)

注意事项:

  • 在使用注解实例的时候一定要保证每条数据都有确定的值:
  • 在使用注解实例的时候给每一个属性赋值
  • 可以在定义注解的时候,给注解中定义的某个属性声明默认值
  • 对于合法的引用类型的数据的默认值不能是null(比如String)
  • 在一种特殊情况下,对注解实例中的属性赋值可以稍作简化:
    a:属性名固定value
    b:当在注解实例中,只需要给value赋值时
    c:@注解类型(10)
@interface AgeConstraint {

  // 定义年龄取值的下界
  int lowerBound() default 18;
  int value();
}


public class Stu {
  //演示value属性的简化赋值
  @AgeConstraint(10)
  public int age;
}

三、注解处理器
注解处理器本身,没有什么特殊的语法,只是通过一些其他方式(比如反射技术),获得所需注解信息,然后根据需求实现特殊功能

元注解:描述注解的注解(描述保留级别)
和Java程序的一致,注解有3种保留级别:

  • SOURCE:注解将被编译器丢弃(class文件中没有)
  • CLASS:注解在class文件中可用,但会被JVM丢弃(内存没有)
  • RUNTIME:JVM在运行时,也会保留注解信息
  • 注解默认情况下的保留级别是CLASS(运行时已经没了)

元注解:描述注解的注解(注解的注解)
常用元注解:
1. @Rentention元注解,来定义我们自己定义的注解的保留级别.
2. @Target元注解,注解可以作用的目标
对于注解而言,可以作用的目标:
1. 整个类 ElementType.TYPE
2. 成员变量 ElementType.FIELD
3. 构造方法 ElementType.CONSTRUCTOR
4. 成员方法 ElementType.METHOD

@Retention(RetentionPolicy.RUNTIME)
// 给注解中数组类型的,属性赋值{}
@Target({ElementType.FIELD, ElementType.METHOD})
@interface AgeConstraint {

  // 定义年龄取值的下界
  int lowerBound();

  //定义年龄取值的上界
  int upperBound();
}

创建学生类的练习

要创建的学生类
class Student {

  @AgeConstraint(lowerBound = 10, upperBound = 25)
  private int age;

  @NameLengthConstraint(nameLimit = 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 + '\'' +
        '}';
  }
}

///约束注解

@Retention(RetentionPolicy.RUNTIME)
@interface NameLengthConstraint {
  int nameLimit();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD})
@interface AgeConstraint {
  // 定义年龄取值的下界
  int lowerBound();
  //定义年龄取值的上界
  int upperBound();
}

//用来创建学生类的注解处理器//
class StudentFactory {

  private Class clz;

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

  /*
       注解处理器中的该方法,帮助我们完成,检查初始化参数,创建Student对象的功能
   */
  public Student getStudent(int age, String name)
      throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    /*
         1. 检查初始化参数
            a. 获取注解实例中的约束值
            b. 根据获取到的注解中的约束,判断初始化参数是否合法
         2. 如果,参数都合法,利用该初始化参数,创建Student对象,并返回,否则抛出异常
     */

    // 判断age初始化参数,是否符合,注解中定义的约束
    judgeAge(age);

    // 判断name初始化参数,是否符合,注解中定义的约束
    judgeNameLength(name);

    //当代码执行到这里,我们就认为,两个初始化参数都满足条件,
    // 利用这个两个合法的初始化参数,创建Student对象并返回(利用反射来完成)
    Student student = generateStudentObj(age, name);

    return student;
  }

  private Student generateStudentObj(int age, String name)
      throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    Constructor constructor = clz.getDeclaredConstructor(int.class, String.class);
    //利用该constructor对象,创建Student对象,并返回

    constructor.setAccessible(true);
    return (Student) constructor.newInstance(age, name);
  }


  private void judgeNameLength(String name) throws NoSuchFieldException {
    // 1. 首先获取目标成员变量
    Field nameField = clz.getDeclaredField("name");

    // 2. 在该成员变量上获取,注解实例,从注解实例中,拿到name成员变量的约束信息
    if (nameField.isAnnotationPresent(NameLengthConstraint.class)) {
      System.out.println("获取到了name成员变量上的注解实例");
      NameLengthConstraint lengthLimit = nameField.getAnnotation(NameLengthConstraint.class);

      //从注解实例中,获取约束信息
      int maxLength = lengthLimit.nameLimit();

      //根据具体的约束的值,来判断,name初始化参数的值,是否满足条件
      if (name.length() > maxLength) {
        throw new IllegalArgumentException("学生的姓名参数非法:姓名长度过长 " + name);
      }
    }
  }

  private void judgeAge(int age) throws NoSuchFieldException {
    // 获取Sudent中age成员变量的约束
    // 1. 首先获取age成员变量
    Field ageField = clz.getDeclaredField("age");

    //为了规避,权限问题
    ageField.setAccessible(true);

    // 2. 获取该成员变量的注解信息
    // ageField.isAnnotationPresent(目标注解类型.class)
    // 帮我们判断,成员变量上是否有目标注解,有返回true,没有就返回false
    if (ageField.isAnnotationPresent(AgeConstraint.class)) {
      System.out.println("获取到了age成员变量上的注解实例");
      //如果该成员变量,有注解,那才获取注解
      // getAnnotation(AgeConstraint.class) 从Field对象表示的成员变量上,获取注解实例
      AgeConstraint ageAnnotation = ageField.getAnnotation(AgeConstraint.class);

      // 从注解实例中获取,注解中定义的属性值, 非常类似于方法调用
      int lowerBound = ageAnnotation.lowerBound();
      int upperBound = ageAnnotation.upperBound();
      //根据需求,判断
      if (age < lowerBound || age > upperBound) {
        // age初始化参数的值不合法
        throw new IllegalArgumentException("学生的年龄参数非法:" + age);
      }
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值