从运行的角度理解Java反射的原理
通过Java的发射机制可以实现一些高级的功能,比如动态生成代理对象、动态生成类、动态配置对象等。在Java的一些框架(如Spring框架)也广泛应用了发射机制,今天我们来聊聊反射的原理。
Java的反射原理涉及到了Java程序的生命周期,如下是一个程序的生命周期图:
如果现在要new一个User对象出来,如下所示:
new User()的时候,首先检查字节码文件有没有被加载过,如果没有加载过就先加载字节码文件到jvm中,这个过程通过classloader实现的。同时,Java在加载的过程中会将字节码文件转换成运行时的数据结构,如下图所示:
此时这个过程就是类的定义,也就是创建类的class类对象,class类对象与字节码是一一对应的,每一个字节码文件都有一个class类对象。
class类对象用来表示当前的字节码文件的结构信息(如当前类中存储的成员变量、构造器、成员方法等等),这些结构信息都是通过数组的形式来表达的。因此,在运行阶段可以通过类的数据结构信息得到对应的成员变量、构造器等等,所以在使用反射的时候我们只需要得到它的class类对象就可以了。
常见的获取class类对象的方法是通过Class.forName("User")来获取User对象的class类对象,当我们得到class类对象之后就可以通过它里面的这些数组来进行读取操作,这就是我们通常看到的发射调用的API。
反射机制的实质就是获取class类对象,而class类对象是类加载器加载了字节码转换过来的。
当用户拿到class类对象就可以完成如下的工作:
(1)在运行时判断任意一个对象所属的类
(2)在运行时构造任意一个类的对象
(3)在运行时得到任意一个类所具有的成员变量和方法
(4)在运行时调用任意一个对象的成员变量和方法
(5)生成动态代理
Java反射机制可以让代码更加具有通用性和复用性,提高开发效率;它也可以操作任意类型的对象,无需知道对象的具体类型;可以实现在运行时动态地获取类的信息和操作对象,使程序更加灵活和易于扩展。
但是,Java的反射由于需要在运行时动态地获取对象信息和方法,存在一定的时间开销,因此性能较低;反射机制由于可以操作任意类型的对象,容易造成安全问题;反射支持动态的获取和操作对象,所以代码的可读性会大大的降低。
反射比直接调用慢,所以有一些常见的优化方法,如使用字节码生成库、缓存Method对象、使用方法句柄等方式来优化。