反射
目录
一、可变参数
从JDK5开始,可以允许方法定义长度可变的参数
// 可以看做数组
public void func(int ... args){
}
- 调用方法时可以将参数罗列传入也可以直接传入数组
- 可变参数的定义只能放在参数列表的最后
- 一个方法最多只能有一个长度可变参数
二、反射
指程序可以访问、检测和修改其本身状态或行为的一种能力,增强了程序的灵活性和
可移植性
1. 反射的作用
- 运行时构造一个任意类的对象
- 运行时判断任意一个类所具有的成员变量和方法
- 运行时调用任意一个对象的方法,包括使用私有声明的方法
2. 反射常用API
- Class类:代表一个类
- Field类:代表类的成员变量
- Method类:代表类的成员方法
- Constructor类:代表类的构造方法
三、Class类
Class类是反射机制的入口
- 每个类都能够获得相应的Class对象
- 可以用于获取与类结构相关的信息
- 继承Object类
1. Class类存放的结构信息
- 类的相关信息:父类,接口,名称等
- 属性
- 方法
- 构造方法
2. 获取Class对象的方式
获取Class的三种方式
1.类.class - 编译阶段 - 编译的文件中读取
2.实例.getClass() - 运行阶段 - new 实例 -> 静态代码块、动态代码块、构造器
3.Class.forName(String name) - 运行阶段 - 静态代码块
- 通过对象获得
Student stu=new Student();
Class clazz=stu.getClass();
- 通过类获得
Class clazz=Student.class;
- 通过forName方法获得
Class clazz = Class.forName("com.qfedu.bean.Student");
3. 获取其他结构的方式
Class clazz = Class.forName("java.lang.Object");
- 获取成员属性
Field fields[ ] = clazz.getDeclaredFields();
- 获取成员方法
Method methods[] = clazz.getDeclaredMethods();
- 获取构造器
Constructor constructors[ ] = clazz.getDeclaredConstructors();
// TODO 获取类的结构信息
Class<Student> studentClass = Student.class;
System.out.println(studentClass.getName());
// 获取构造器的对象
Constructor[] constructor1 = studentClass.getConstructors();
Constructor[] constructor2 = studentClass.getDeclaredConstructors();
// 获取成员变量
Field[] field1 = studentClass.getFields();
Field[] field2 = studentClass.getDeclaredFields();
// 获取方法
// 包括超类中声明的方法,没有私有方法
Method[] method1 = studentClass.getMethods();
// 可以查看私有方法,不包括本类之外的
Method[] method2 = studentClass.getDeclaredMethods();
// TODO 获取类的相关信息
Class<Student> studentClass = Student.class;
// 父类信息
System.out.println(studentClass.getSuperclass());
// 实现的接口
System.out.println(studentClass.getInterfaces()[0]);
// 所在的包
System.out.println(studentClass.getPackage().getName());
// TODO 构造器 -> 返回新的实例 (企业级封装)
// 1.获得Class
Class<?> studentClass = Class.forName("com.qfedu.bean.Student");
// 2.遍历构造器
Constructor<?>[] constructors = studentClass.getConstructors();
// 定义一个map结构记录每种构造器对应的参数列表
Map<String, Class<?>[]> arg = new HashMap<>();
// constructor:每次获取到的构造器
for (Constructor<?> constructor : constructors) {
// 定义一个数组记录当前构造器的参数列表(顺序及类型)
Class<?>[] temp = new Class[constructor.getParameterTypes().length];
// 定义一个变量记录顺序 - 同时控制下标
int i = 0;
// 3.遍历参数列表
// clazz:每次获取到相应的类型
for (Class<?> clazz : constructor.getParameterTypes()) {
// 将当前参数类型放入数组
temp[i++] = clazz;
}
// 将当前构造器对应的参数列表存入map
// key:构造器描述信息 - 权限修饰符 构造器全称 [参数列表]
// value:构造器的参数列表
arg.put(constructor.toString(), temp);
}
// 4.获得到一个确切的构造器对象 - 使用通用的方式记录参数列表以及使用的需要进行确定
// 定义一个字符串 - 准备记录匹配到的构造器描述信息(也可能没有匹配)
String key = "";
// list集合中记录了传入的零散信息 - 如:接收的数据
ArrayList<Class<?>> condition = new ArrayList<>();
// 添加模拟数据
condition.add(String.class);
condition.add(Integer.class);
for (Map.Entry<String, Class<?>[]> entry : arg.entrySet()) {
boolean flag = true;
for(int i = 0;i < entry.getValue().length;i ++) {
if (!entry.getValue()[i].getName().equals(condition.get(i).getName())) {
flag = false;
}
}
if (flag) {
key = entry.getKey();
}
}
System.out.println(key);
// 通过参数类型来判断 - 通过value检索 - 匹配相应key - 遍历entrySet
// 向不定参数的方法传递参数时 - 数量不确定时
// 1.罗列方式传入
// 2.数组方式传递
Constructor<?> constructor2 = studentClass.getConstructor(arg.get(key));
// 4.通过无参的构造器返回新的实例
System.out.println(constructor2.newInstance("sand",20));
四、反射常用操作
1. 动态创建对象
- 使用newInstance()方法,仅能调用无参构造方法
Class clazz=Class.forName("com.qfedu.Student");
Object obj=clazz.newInstance();
- 使用Constructor的newInstance()方法,可以调用所有的构造方法
Class clazz=Class.forName("com.qfedu.Student");
Constructor<Student> constructor = clazz.getConstructors()[0];
Object obj=constructor.newInstance();
2. 动态操作属性
- 获取所有的Filed对象:getDeclaredFields()/getFields()
- 通过对象的简称获得Field对象:getDeclaredField(String name)/getField(String name)
3. 动态执行方法
- 获取所有的Method对象:getDeclaredMethods()/getMethods()
- 通过方法名称和参数列表获得Method对象:getMethod(String name, Class... parameterTypes)/getDeclaredMethod(String name, Class... parameterTypes)
- 使用invoke()执行某个实例的方法