注解
- 注解的定义
- 注解的使用
- 注解的处理
一、注解的定义
public @interface 注解名{
定义体
}
- 注解的定义和类的定义以及接口很像
- 就像类,接口在JAVA中地位一样,他也代表数据类型,定义的一个注解就是一种数据类型,一个注解数据类型定义了:具体包含多少条,具体的信息,以及每种信息是怎样的
- @必不可少
- 注解之间不能继承
- 在定义中,一条信息,合法的取值有:
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);
}
}
}
}