深入理解反射原理

导读:本篇文章讲解 深入理解反射原理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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
对象等,就像物体通过反射在镜子里面生成
镜像一样,所以把这种方式称为反射,就很好理解了。

深入理解反射原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/111849.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!