Java反射机制

2021/02/13 Java初级进阶 共 2633 字,约 8 分钟
闷骚的程序员

1. 反射

  1. 反射机制是JDK底层提供的一种高级特性机制,它是一种机制,是种底层技术,是种手段,而不是一种设计模式。
  2. 反射虽然不是一种设计模式,但是很多设计模式都离不开反射。
  3. 反射机制底层依赖的是JDK提供的动态加载技术,从代码层面来讲,依赖的就是JVM默认为任何一个目标类生成的唯一一个class对象。
  4. 动态加载技术和动态字节码技术是体现java语言动态性的两个重要底层技术:
    (1)、反射底层的动态加载技术,提供了操作动态JVM中某个对象、或者为动态JVM生成一个新对象的功能。常用作动态的操作对象,或者动态的为JVM追加功能。
    (2)、动态字节码技术,提供了为JVM生成动态的.class字节码文件的功能;常用作动态增加新功能。
    tips:
    反射是动态的加载目标类和操作目标对象,动态表示JVM此时正在运行。
    动态字节码是指为动态JVM生成动态的字节码文件,即生成的
    .class字节码是在JVM的内存中虚拟存储的。
    区别是:
    动态字节码技术,讲的是字节码的生成过程是在一个正在运行当中的JVM中动态进行的,生成的字节码是在JVM内存中虚拟缓存的,而不是java编译器预编译后产生的.class文件。
    动态加载技术,讲的是目标类的装载过程是在一个正在运行当中的JVM中动态进行的,一个正在运行的JVM可以主动的装载一个新的目标类,这个目标类同样需要我们提前写好源码,也同样需要编译器先编译成
    .class字节码后,才可以被动态的JVM装载进去。
  5. 反射既可以动态装载目标类,也可以像其他普通类一样进行静态装载。
    (1)、获取目标类的权限定名称(该名称可以是动态传入的,比如Class.forName()方法传入)。
    (2)、开始装载目标类。
    (3)、如果目标类已经被装载,即正在JVM中运行,则跳过装载,直接获取目标类的class对象。
    (4)、如果目标类没有被装载,即需要装载的目标类也是新编写的,则开始装载目标类,和JVM静态装载普通类一模一样;装载完成后,直接获取目标类的class对象。

    总结:
    上面描述太绕了。
    说白了就是,反射提供为动态运行的JVM装载目标类的一种新形式。
    如果目标类已经提前编写好了,则此时通过反射操作目标类,实际上该目标类已经被静态装载到JVM中了。
    反射能为动态JVM装载目标类的方式实际上只有一种:Class.forName(“目标类的权限定名”)。只有通过这种方式,才能为正在运行的JVM装载一个新的目标类,而且不用import该类,动态JVM会寻找到目标类的class文件后动态进行装载。

2. 什么是反射?

反射就是把Java类中的各个成分映射成一个个对应的java对象。
例如:一个类有成员变量Field,有构造方法Construct,有成员方法Method,也有类自己等信息,反射就是通过另外的对象来描述一个类中的各种信息。
利用反射技术可以对一个目标类进行解剖,将目标类中的各个成员组件分别映射成对应的java对象来描述。
比如通过一个class对象来描述某一个目标类比如Person的组成信息;
通过一个method对象来描述目标类Person中的普通方法;
通过一个field对象来描述目标类Person中的普通成员;
通过一个construct对象来描述目标类Person中的构造方法;
等等诸如此类。

3. 为什么使用反射?

需求:
我公司定义了一组接口,然后第三方公司按照我公司的接口实现好了一套功能,交给了我们公司,但是我们公司的项目已经结束。

  1. 如何实现动态加载第三方公司提供的功能,而不用修改源代码?
  2. 如果追加某些新代码想直接调用当前线上的某些对象的功能,又不想停止生产环境,怎么搞?
    回答:
  3. 对于第一个问题,要求当前已经上线的项目在开发之初,就应该使用反射,预留反射加载的目标类的权限定名。
    一般通过配置文件来配置需要动态加载的目标类的权限定名称,和要执行的目标类中的方法等信息,随后便可以动态的解析文件来为运行中的JVM直接加载这些目标类并进行动态调用。
    这样当第三方公司实现好了之后就不用修改生产代码,直接将新功能的代码丢上去并修改配置文件即可为动态JVM加载目标类,追加新功能。
  4. 第二个问题,更简单,不用停止生产环境,直接通过反射动态获取正在运行的JVM中的任何java对象,然后便可以随便操作它。

4. 反射的作用

  1. 在JVM运行时判断任何一个对象所属的类型,即只需要指定目标类的权限定名称便可以获取到目标类的类型信息。
  2. 在JVM运行时获取目标类的对象,动态访问目标对象的属性、方法和构造方法等(获取后你想干啥就干啥);而不用停止生产环境。
  3. 在JVM运行时,新追加一部分功能,直接通过反射将新追加的功能交给动态JVM执行,而不用修改原有的生产代码(生产代码需要提前预留反射的入口)。

5. 静态加载和动态加载

5.1 静态加载

需要重新启动JVM进行目标类加载的过程称为静态加载。
A对象和B对象通过代码显式引用,确定编译状态,编译后在class字节码文件中也会保持这种静态的引用关系。
静态加载需要明确的import导入目标类,在编译阶段即确认目标类和当前类的关系,然后这种关系经过编译后,才会由JVM装载进去,即静态加载类和类之间的依赖关系在编译阶段就是确定的,每次都需要JVM重新启动去重新加载新的依赖关系。

5.2 动态加载

反射为动态加载提供了一种可能性,即使用 Class.forName()可以动态的加载某些位置的Class。
想要为正在运行的JVM提供某些新的功能类,这时反射的 Class.forName() 就提供了无限可能。
通过 Class.forName() 可以为正在运行的JVM装载任何经过编译的class,而这么强大的功能只需要传入目标类的权限定名,不需要显式import目标类,不需要预先知道目标类的任何结构。
动态加载所需要的目标class的整个编译过程是完全独立的,你只需要提供一批编译后的字节码文件,正在运行的JVM便会自动加载这些class,而丝毫不用做任何停止动作。

6. 静态字节码和动态字节码

6.1 静态字节码

自己编写.java源文件,然后经过java编译器预编译生成字节码文件.class,存放在服务器的指定目录中,这些字节码文件是看得见摸得着的。

6.2 动态字节码

程序运行期间,动态的为某个接口的实现类,或者某个类的子类生成字节码文件*.class,这个字节码文件是在JVM运行期间由JVM动态生成的,服务器上看不见摸不着,不落地服务器,只存在于动态运行的JVM内存中,JVM停止即释放。

文档信息

Search

    Table of Contents