目录


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);
    }
}
©本文为原创文章,著作权归博主所有,转载请联系博主获得授权

发表评论