实现如下需求:
定义一个Student类,包含name和age两个成员
name中包含的字符个数不得超过指定值(注解里有具体的约束条件信)
age必须在指定范围内(注解->具体的约束条件信息 )
name和age都满足条件才能创建Student对象,否则抛出异常。(该效果由注解处理器来实现)
Student类
public class Student {
//实例化注解,表明下面的变量赋值时,要符合注解的限制条件,才能赋值
@AgeBound(ageMin = 18, ageMax = 25)//该注解只修饰age
private int age;
@NameBound(nameLimit = 5)//名字长度不能超过5
private String name;//private,防止用户随意修改成员变量值
//private 是防止用户直接用构造方法new对象,并给成员变量赋值,导致age的范围不符合注解要求
//让用户只能用我们设定的方法,创建对象并赋值,
// 我们设定的方法里面有注解处理器,能分析注解,从而限制age范围
private Student() {
}
private Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})//设置注解能用在什么地方
@Retention(RetentionPolicy.RUNTIME)
//把注解的存活时间改成,运行时存活
@interface AgeBound {//用来限制年龄的注解
// 定义2个属性值
int ageMin();//最小年龄
int ageMax();//最大年龄
}
@Retention(RetentionPolicy.RUNTIME)
@interface NameBound {//用来限制姓名的注解
int nameLimit();//名字长度
}
真正用来创建对象的工厂类,里面有自定义注解处理器
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class StudentFactory {
private Class studentCls;
public StudentFactory() {
this.studentCls = Student.class;
}
public Student getStudent(int age, String name) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// age校验
judgeAge(age);
// 名字的校验
judgeName(name);
// 由于构造方法是私有的,所以只能通过反射获取私有构造器,并把它设为可用,创建一个学生对象并返回
// 通过反射去实例化
// 获取构造函数
Constructor declaredConstructor =
studentCls.getDeclaredConstructor(int.class, String.class);
// 暴力方式
declaredConstructor.setAccessible(true);
Student student = (Student) declaredConstructor.newInstance(age, name);
return student;
}
//isAnnotationPresent(),getAnnotation(),只能由Class、Method、Field对象调用
private void judgeName(String name) throws NoSuchFieldException {
// 通过字节码文件对象获取年龄的成员变量
Field nameField = studentCls.getDeclaredField("name");
// 判断是否使用了注解
boolean annotationPresent = nameField.isAnnotationPresent(NameBound.class);
if (annotationPresent) {
// 获取注解实例
NameBound nameBound = nameField.getAnnotation(NameBound.class);
// 获取具体的值
int nameLimit = nameBound.nameLimit();
//校验名字是否满足条件
if (name.length() > nameLimit) {
throw new IllegalArgumentException("名字不合法,长度为:" + name.length());
}
}
}
private void judgeAge(int age) throws NoSuchFieldException {
Field ageField = studentCls.getDeclaredField("age");
// 判断age成员变量是否使用了注解,参数是对应的注解类型的字节码文件对象
boolean annotationPresent = ageField.isAnnotationPresent(AgeBound.class);
if (annotationPresent) {
// 获取注解实例(对象)
AgeBound ageBound = ageField.getAnnotation(AgeBound.class);
// 获取注解里的属性值
int ageMax = ageBound.ageMax();
int ageMin = ageBound.ageMin();
if (age < ageMin || age > ageMax) {
throw new IllegalArgumentException("年龄不合法 :" + age);
}
}
}
}
主类
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, NoSuchFieldException, InstantiationException, IllegalAccessException {
Student student = new StudentFactory().getStudent(20, "李四");
System.out.println(student);
//输入不合法的年龄,姓名长度,运行时会报错
Student student1 = new StudentFactory().getStudent(100, "李四3333");
System.out.println(student1);
}
}