目录
1.什么是反射?
反射就是将类的各个组成部分封装成其他对象,这就是反射机制。
2.反射的好处
- 可以在程序运行过程中操作被封装的对象
- 可以解耦,提高程序的可扩展性
3.java代码的执行底层原理
在学习反射之前我们要先了解一下java代码的执行过程:java代码本质上就是.java文件,它经过java编译器的编译(命令是javac xxx.java)变为.class字节码文件(二进制文件)(存在于硬盘上),然后经过jvm(java虚拟机)内部的一系列执行过程(在内存中)就变成了底层操作系统可执行的操作指令。
Java虚拟机的主要任务是装载class文件,并执行其中的字节码,不同的Java虚拟机中,执行引擎可能有不同的实现。
4.反射的原理
java源码在编译后被jvm的类加载器加载进内存,经过“反射”这一机制后,java源码中所有的属性(成员变量,构造方法,方法等)在内存中都一一对应,即像镜子一样把源码的内容反射进内存,同时jvm自动创建了一个Class对象,我们可以通过这个Class对象来调用反射进来的属性,通过控制这些属性来间接控制java代码中成员变量,方法,构造方法等。
要说明一下这里java编译后的字节码文件中的各个成员变量,方法,构造方法等在jvm内存中都有对应,成员变量对应Field方法 ,构造方法对应Construct方法,成员方法对应Method。
5.反射的使用
反射的使用分两步,一是将字节码文件加载进内存,返回Class对象;二是调用Class对象的相应方法执行目的操作。
步骤一:获取Class对象
- 方法1:Class.forname(“全类名”):将字节码文件加载进内存,返回Class对象,多用于配置文件,将类名定义在配置文件中。读取文件,加载类;
- 方法2:类名.class:通过类名的属性class获取,多用于参数的传递;
方法3:对象.getClass():getClass()方法在Object类中定义着,多用于对象的获取字节码的方式。
步骤二:调用Class对象的方法进行相关的目的操作
- 第一步先获取要操作的方法
- 第二步是对获取到的方法进行目的操作。简单说就是先用Class对象调用方法,这会返回相应方法的对象,这就是第一步的先获取要操作的方法。然后要用Construct创建实例对象,用上一步获得的方法的对象调用相关的方法,将Construct创建的对象传参进去即可。
Class对象可获取的方法:
1、获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
//带Declared的方法都表示不考虑修饰符,下面的方法也是这样,不再说明
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
2、获取成员方法们
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
3、获取全类名
String getName();
对获取的方法进行操作
1、Constructor:构造方法
* 创建对象:
//在使用构造方法时可以调用newInstance方法创建目的类的对象
* T newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
2、Field:成员变量
* 操作:
1. 设置值
* void set(Object obj, Object value)
2. 获取值
* get(Object obj)
//在使用带Declared的方法时由于jvm的检查可能会报错,这时可以用下面的方法来忽略访问权限修饰符的安全检查
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
3、Method:方法对象
* 执行方法:
* Object invoke(Object obj, Object... args) //obj为new出来的实例化对象,args为方法参数
* 获取方法名称:
* String getName:获取方法名
举例说明
@SpringBootTest
@RunWith(SpringRunner.class)
public class ReflectTest {
@Test
public void testField() throws Exception {
// 获得class对象
Class clazz = Class.forName("com.ruoyi.project.system.domain.SysUser");
// 获取class对象的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
// class对象调用newInstance()方法生成类的实例对象
Object o = clazz.newInstance();
// 获取class指定属性
Field userName = clazz.getDeclaredField("userName");
// 取消Java语言的访问权限检查
userName.setAccessible(true);
userName.set(o, "mwj");
System.out.println(o);
}
@Test
public void testConstructor() throws Exception {
// 获得class对象
Class<SysUser> clazz = SysUser.class;
// 获取class对象的构造函数
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
// 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成类的实例对象
Constructor<SysUser> constructor = clazz.getConstructor(Long.class);
SysUser sysUser = constructor.newInstance(24L);
System.out.println(sysUser);
}
@Test
public void testMethod() throws Exception {
// 获得class对象
Class<SysUser> clazz = SysUser.class;
// 获取class对象的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
// 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成类的实例对象
Constructor<SysUser> constructor = clazz.getConstructor(Long.class);
SysUser sysUser = constructor.newInstance(24L);
// 返回class对象对应类的、带指定形参列表的public方法
Method getUserId = clazz.getMethod("getUserId");
// 调用指定的函数并传递参数
Long userId = (Long) getUserId.invoke(sysUser);
System.out.println(userId);
}
}
最新回复