深入理解反射原理

1. 反射的缘起

1.1 什么是反射及反射机制?

反射就是把 Java 类中的 各个成分映射成一个个的 Java 对象
即在运行状态中:
  1. 对于任意一个类,都能够知道这个类的所有属性和方法;
  2. 对于任意一个对象,都能够调用它的任意一个属性和方法。
这种动态获取信息以及动态调用对象方法的功能叫 Java 的反射机制。

1.2 为什么会产生反射?

其实看到上面这么概念化的定义,我们还是一头雾水。那我们先抛开概念,来看看不使用反射的情况下,我们是怎么写程序的?我们先来写一个例子:
// 创建一个动物对象,汤姆猫
Animal tomcat = new Animal();
// 调用奔跑方法,传入时间,获取奔跑的距离
double distance = tomcat.run(2d);
那我们再来看看使用反射,我们是怎么写的?
// 获取动物类的Java对象(类的反射对象,为什么Class对象要叫类的反射对象,后面讲类加载的时候会说
明)
Class clz = Class.forName("com.dblones.reflect.example.Animal");
// 获取奔跑方法的Java对象(方法的反射对象)
Method method = clz.getMethod("run", double.class);
// 获取构造方法的Java对象(构造方法的反射对象)
Constructor constructor = clz.getConstructor();
// 通过反射实例化对象
Object object = constructor.newInstance();
// 通过反射调用奔跑方法
Object distance = method.invoke(object, 2d);
看到这儿,你可能会说,本来 2 行的代码,被你搞成了 5 行,简单的事情复杂化了,这反射看着没啥用啊!别急,如果你仔细看得话,会发现代码虽然变多了,但是我们没有 import Animal 类,而是以字符 串的方式传入 Animal 类,进一步如果把 "com.dblones.reflect.example.Animal" 这个字符串放到外部,通 过配置文件或者执行传参的方式传入的话,程序就可以动态传入来执行了,那么这样到底有啥用呢?别 着急,慢慢往下看。
我们大家都知道, Java 程序要运行起来,需要经过一个 javac 编译为 class 字节码,然后通过 java 解释执行 两个过程。 Java 是一门静态类型语言,变量的类型在编译之前就需要确定,不然编译都不会过,这样虽 然有很多好处,但是也限制了程序的灵活性,比如说,上面的程序,实例化对象的时候类型是 Animal 如果将来我想改成 Animal 的子类 Cat 的时候,就需要改代码重新编译才行。再例如,如果 Animal 这个类 是运行过程中动态生成的,那我就无法在程序中使用了。
介于静态语言以上的缺点,很多静态语言就扩展出了反射机制,能够动态获取信息以及动态调用对象方法。 Java 的反射以及反射机制就是这样产生的,其实, Java 基础技术中的代理,注解也都是依托反射才 能得以实现并应用广泛,另外我们常用的 Spring myBatis 等技术框架也都是依托反射才能得以实现。

1.3 为什么叫反射?

说到反射,我们最早接触的反射就是初中物理里面光的反射,光的反射是指光在传播到不同物质时,光在两种物质分界面上改变传播方向又返回原来物质中的现象。光的反射带来的一个现象就是镜面成像。 那这个跟我们 Java 里面的反射有什么关系呢?

 要说清这个问题,我们就要从JVM的类加载以及OOP-KLASS模型说起。

JVM 中,使用了 OOP-KLASS 模型来表示 java 对象,即:
  1. JVM在加载class到内存时,会先解析class字节码资源,创建类元数据即instanceKlass对象(C++对象),包括常量池、字段、方法等,存放在方法区(hotspot虚拟机1.8以后的版本对应的就是元空间,1.8以前的版本就是永久代)
  2. new一个对象时,JVM会创建instanceOopDesc实例,来表示这个对象,存放在堆区,其引用,存放在栈区或堆区;它用来表示对象的实例信息,看起来像个指针,实际上是藏在指针里的对象;instanceOopDesc对应Java中的对象实例;
  3. HotSpot并不把instanceKlass暴露给Java,而会另外创建对应的instanceOopDesc来表示 java.lang.Class对象,并将Class对象称为instanceKlass对象的“Java镜像instanceKlass对象有一个属性 _java_mirror 指向Class对象,klass持有指向oop引用( _java_mirror便是该instanceKlassClass象的引用 )
  4. 要注意,new操作返回的instanceOopDesc类型有指针指向instanceKlass,而instanceKlass指向了对应的类型的Class实例的instanceOopDesc;有点绕,简单说,就是Person实例—>PersoninstanceKlass—>PersonClass。实际上调用 对象.getClass() 方法时,就是通过 对象先找到InstanceKlass对象,然后再通过 _java_mirror找到Class对象的。
instanceKlass 对象和 Class 对象都是描述 class 字节码的数据对象,只不过 instanceKlass 对象在方法区(Hotspot 实现对应是永久代或元空间,取决于 JDK 版本 ) Class 对象在堆区, instanceKlass 对象是 C++ 象, Class 对象是 Java 对象 ( 当然物理上是 C++ instanceOopDesc 对象,逻辑上是 Java 对象 ) 。从 Class 象到 Class 对象生成的 Constructor 对象, Method 对象 , Field 对象等,就像物体通过反射在镜子里面生成 镜像一样,所以把这种方式称为反射,就很好理解了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

出世&入世

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值