Java反射到底是什么?

反射是我学习Java时遇到的最大障碍,因为太抽象了。

我当时是跟着网络上的培训班视频自学的,视频里的老师说:类是对现实事物的抽象,而Class是对类的抽象。这句话对于刚开始学习Java的人来说,无异于天书。我当时的理解是:根据类,可以创建对象,所以类是比对象高一级的抽象。如果要对类进一步抽象,它应该是什么形式呢?

我无法想象。在我的认知里,Java除了类还是类,类层级的抽象似乎已经到尽头了。

如何对类进行抽象?

许多年后,再次回想那段经历,我发现自己当时陷入了一个思维陷阱,误以为"用类抽象类"是一个死循环,把自己困在里面了。

其实,换个角度这个问题就迎刃而解了。Java用类对世间万物进行抽象,包括人、动物、甚至空气、键盘、鼠标,那么用Java语言编写的类文件(xxx.java)不也是世间万物的一种吗?是不是也能用类表示?老师说的那句话没错,但更准确的说法是:

对于Java而言,类是对现实事物的抽象,其中有一个Class类用来描述Java类(文件)

Class类也是一个类,只不过它描述的事物比较特殊:Java类

按照这个思路,我们尝试写一个Class类,用来描述Java中的"类"。好,想法有了,怎么开始呢? 所谓抽象,就是抽取相像的。什么叫相像?单个事物肯定不能说相像,因为相像是相互比对后得出的结论。所以,我们至少要先编写两个类。

抽象最忌讳一上来就关注细节,这样是很难得到一个好的抽象模型的。反之,在设计之初应该观其大略,从大处着手"找共性"。

它们都是Class,它们都有Constructor、Field、Method

啊?这也太粗粒度了吧!别急,再慢慢细化就是了:

方法可以细分为:访问修饰符、返回值类型、方法名、参数列表

如果你足够耐心,还可以继续细化,比如Parameter可以进一步细分为:修饰符、参数类型、参数名、参数索引(第几个参数)。由于Java已经替我们做好抽象,这里就不再继续重复劳动了。

其他的就不列举了,总之最终我们会得到很多个大小模型,这些模型相互协作共同描绘了"Java类"这一抽象。

Class对象

什么是Class对象?简单来说,就是Class类的实例。

Java有一个Class类(上面截图第一个),那么按理说我们应该可以new一个Class对象。但Class类是个狠角色,它不仅对自己进行了阉割(final class,不允许继承),还隐藏了构造器(final constructor,不允许new)。也就是说,正常途径下没有人能创建Class对象,也不会有Class的子类。

官方说得很清楚:Only the Java Virtual Machine creates Class objects.

那虚拟机创建的Class对象长啥样呢?

在Java中,每个类在首次加载时都会有一个对应的Class对象被创建。

当你执行new User()时,JVM 会检查是否已经加载了User类:

  • 如果没有加载,它会先加载User类,并创建User类的Class对象,然后根据该对象创建User类的实例。
  • 否则,直接找到User类的Class对象并创建User类的实例。

也就是说,我们一直以为new User()的步骤是 User类 ==> User对象,但实际上是 User类 ==> User类的Class对象 ==> User对象。 注意,这里有一个反直觉的地方:

你说Class类无法直接通过new Class()创建Class对象,只能由JVM创建Class对象。好,我虽然不理解,但也认了。然而,为什么JVM创建的是Student Class对象?一个Class类,怎么会创建出Student Class对象呢?

我可没说JVM创建了Student Class对象。上面图中所说的Class对象(Student),指的是这个Class对象包含的信息来自Student类。

这样一来,顺便也解答了为什么Class类无法直接创建Class对象这个谜团:

Class对象是用来封装类信息的,直接new Class()得到的是空对象,所以干脆禁止了

也就是说,Class对象是用来封装类信息的,直接new Class()并不能与任何类产生关联,最终得到的是没有实际意义的空对象。于是Java干脆把new Class()这条路给堵了,当类加载时由JVM创建Class对象。这样不仅能由JVM保证同一个类只有一个Class对象(类加载是单线程的),还能保证Class对象是有意义的(包含具体类的信息)。

其他的Method对象、Field对象、Constructor对象也是同样的道理,分别是用来封装方法信息、字段信息、构造函数信息的。

重新理解反射

到这,反射就很好理解了。Java的反射机制由reflect package提供支持:

我们可以将反射机制的构成简单理解为:Class信息 + 获取/操作这些信息的API。Java将类的信息抽象成各种数据,并提供API让我们获取这些数据。

这里只提一点:最好把Class/Method/Field等对象理解为"执行器"。 以Method对象为例,它就像一个方法执行器,当你想要执行某个对象的某个方法时,根本不用求这个对象本身,而是去找到Method,把对象+参数告诉它,让Method帮你执行。

从companyClass获取echoMethod(执行器),传入company对象+参数,执行方法

这就像什么呢?有一个老师傅,一直不肯使出本门剑法的最后一招。正常来说,你要求他:师傅,请你调用一下剑法的最后一招让徒孙开开眼吧!现在有了反射,你就不用求他了,直接找到Method,把师傅(Object)绑了,并且找到师傅的剑(Param),人和剑一块儿都塞给Method,然后Method催动内力,逼着师傅拿着剑把最后一招耍出来了。

如果不是public的字段和方法,需要在操作前调用setAccessible(true)

大家有没有看过成龙的《醉拳》,酒鬼师傅为了教成龙功夫,用两根竹竿拴住他的双手,操作他做出各种招式。Method也是如此,原本是对象自己调用方法,现在则是反射调用方法。但前提是,必须要有对象,你才能操(qiang)作(po)对象执行方法。

还有一个问题,常常让人感到困惑:我们把对象和参数传给Method让它帮忙执行,这固然没错,但既然已经有目标对象了,为什么不直接调用对象的方法呢?

这是因为你看的角度不对。还是以上面的醉拳师傅为例,在还不知道是哪位徒弟、甚至没有徒弟的时候,师傅就要先准备好这一套训练方法。后面如果有人要学醉拳,直接套上竹竿就可以了。也就是说,反射是一种面向未来的通用处理方式,未来虽未来,但我的代码已经存在!

arduino 复制代码
class 通用设计 {
    public void 通用操作(obj ,param) {
        // 操作1 2 3
        method.invoke(obj, param); // 这里如果写成user.sing(),就不通用了
        // 操作 4 5 6
    }
}

反射是非常强大的元编程能力,是很多中间件和框架的底层支撑。

花了半年时间实现了GOF23种设计模式,gitee已开源:设计模式那些事儿

相关推荐
xoxo-Rachel6 分钟前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql
乌啼霜满天2498 分钟前
JDBC编程---Java
java·开发语言·sql
微信-since8119216 分钟前
[ruby on rails] 安装docker
后端·docker·ruby on rails
色空大师20 分钟前
23种设计模式
java·开发语言·设计模式
闲人一枚(学习中)21 分钟前
设计模式-创建型-建造者模式
java·设计模式·建造者模式
2202_7544215438 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
蓝染-惣右介41 分钟前
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
java·数据库·tomcat·mybatis
小林想被监督学习42 分钟前
idea怎么打开两个窗口,运行两个项目
java·ide·intellij-idea
HoneyMoose44 分钟前
IDEA 2024.3 版本更新主要功能介绍
java·ide·intellij-idea