1. 反射的缘起
1.1 什么是反射及反射机制?
反射就是把
Java
类中的
各个成分映射成一个个的
Java
对象
。
Java
类中的
各个成分映射成一个个的
Java
对象
。
即在运行状态中:
- 对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个属性和方法。
这种动态获取信息以及动态调用对象方法的功能叫
Java
的反射机制。
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”
这个字符串放到外部,通
过配置文件或者执行传参的方式传入的话,程序就可以动态传入来执行了,那么这样到底有啥用呢?别
着急,慢慢往下看。
2
行的代码,被你搞成了
5
行,简单的事情复杂化了,这反射看着没啥用啊!别急,如果你仔细看得话,会发现代码虽然变多了,但是我们没有
import Animal
类,而是以字符
串的方式传入
Animal
类,进一步如果把
“com.dblones.reflect.example.Animal”
这个字符串放到外部,通
过配置文件或者执行传参的方式传入的话,程序就可以动态传入来执行了,那么这样到底有啥用呢?别
着急,慢慢往下看。
我们大家都知道,
Java
程序要运行起来,需要经过一个
javac
编译为
class
字节码,然后通过
java
解释执行
两个过程。
Java
是一门静态类型语言,变量的类型在编译之前就需要确定,不然编译都不会过,这样虽
然有很多好处,但是也限制了程序的灵活性,比如说,上面的程序,实例化对象的时候类型是
Animal
,
如果将来我想改成
Animal
的子类
Cat
的时候,就需要改代码重新编译才行。再例如,如果
Animal
这个类
是运行过程中动态生成的,那我就无法在程序中使用了。
Java
程序要运行起来,需要经过一个
javac
编译为
class
字节码,然后通过
java
解释执行
两个过程。
Java
是一门静态类型语言,变量的类型在编译之前就需要确定,不然编译都不会过,这样虽
然有很多好处,但是也限制了程序的灵活性,比如说,上面的程序,实例化对象的时候类型是
Animal
,
如果将来我想改成
Animal
的子类
Cat
的时候,就需要改代码重新编译才行。再例如,如果
Animal
这个类
是运行过程中动态生成的,那我就无法在程序中使用了。
介于静态语言以上的缺点,很多静态语言就扩展出了反射机制,能够动态获取信息以及动态调用对象方法。
Java
的反射以及反射机制就是这样产生的,其实,
Java
基础技术中的代理,注解也都是依托反射才
能得以实现并应用广泛,另外我们常用的
Spring
、
myBatis
等技术框架也都是依托反射才能得以实现。
Java
的反射以及反射机制就是这样产生的,其实,
Java
基础技术中的代理,注解也都是依托反射才
能得以实现并应用广泛,另外我们常用的
Spring
、
myBatis
等技术框架也都是依托反射才能得以实现。
1.3 为什么叫反射?
说到反射,我们最早接触的反射就是初中物理里面光的反射,光的反射是指光在传播到不同物质时,光在两种物质分界面上改变传播方向又返回原来物质中的现象。光的反射带来的一个现象就是镜面成像。
那这个跟我们
Java
里面的反射有什么关系呢?
那这个跟我们
Java
里面的反射有什么关系呢?
要说清这个问题,我们就要从JVM的类加载以及OOP-KLASS模型说起。
在
JVM
中,使用了
OOP-KLASS
模型来表示
java
对象,即:
JVM
中,使用了
OOP-KLASS
模型来表示
java
对象,即:
- JVM在加载class到内存时,会先解析class字节码资源,创建类元数据即instanceKlass对象(C++对象),包括常量池、字段、方法等,存放在方法区(hotspot虚拟机1.8以后的版本对应的就是元空间,1.8以前的版本就是永久代);
- 在new一个对象时,JVM会创建instanceOopDesc实例,来表示这个对象,存放在堆区,其引用,存放在栈区或堆区;它用来表示对象的实例信息,看起来像个指针,实际上是藏在指针里的对象;instanceOopDesc对应Java中的对象实例;
- HotSpot并不把instanceKlass暴露给Java,而会另外创建对应的instanceOopDesc来表示 java.lang.Class对象,并将Class对象称为instanceKlass对象的“Java镜像”,instanceKlass对象有一个属性 _java_mirror 指向Class对象,klass持有指向oop引用( _java_mirror便是该instanceKlass对Class对 象的引用 );
- 要注意,new操作返回的instanceOopDesc类型有指针指向instanceKlass,而instanceKlass指向了对应的类型的Class实例的instanceOopDesc;有点绕,简单说,就是Person实例—>Person的instanceKlass—>Person的Class。实际上调用 对象.getClass() 方法时,就是通过 对象先找到InstanceKlass对象,然后再通过 _java_mirror找到Class对象的。
instanceKlass
对象和
Class
对象都是描述
class
字节码的数据对象,只不过
instanceKlass
对象在方法区(Hotspot
实现对应是永久代或元空间,取决于
JDK
版本
)
,
Class
对象在堆区,
instanceKlass
对象是
C++
对
象,
Class
对象是
Java
对象
(
当然物理上是
C++
的
instanceOopDesc
对象,逻辑上是
Java
对象
)
。从
Class
对
象到
Class
对象生成的
Constructor
对象,
Method
对象
, Field
对象等,就像物体通过反射在镜子里面生成
镜像一样,所以把这种方式称为反射,就很好理解了。
对象和
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