Java学习随笔:反射为什么既强大又危险(2026-10-11)
刚开始学 Java 的时候,我以为方法调用只能靠“对象点方法”这一种方式。后来接触到框架,才发现很多代码里根本没有直接调用,而是先拿到类名,再通过反射去创建对象、调用方法。那一刻我才明白,原来 Java 程序可以在运行时“看见自己”。
反射最基础的能力是拿到一个类的信息。通过 Class 对象,我们可以知道这个类有哪些字段、哪些方法、哪些构造器,甚至可以知道它实现了哪些接口、继承了哪个父类。有了这些信息,就可以在运行时创建实例、读写字段、调用方法,哪怕这些成员原本是私有的。正因为如此,很多通用框架才得以实现:Spring 根据注解自动装配对象,MyBatis 把查询结果映射成实体,Jackson 把 JSON 转成对象,背后都有反射在起作用。
但反射的强大也带来了代价,大致可以分成三块。第一是性能。通过反射调用方法,要经过一系列检查和查找,编译器无法像直接调用那样做内联等优化,所以速度会慢一些。单次调用可能只差几纳秒,但在热点路径上反复使用,累积起来就相当可观。好在这几年虚拟机对反射做了不少优化,再加上方法句柄等替代方案,性能差距已经缩小了很多。
第二是安全性。反射可以绕过访问控制,直接读写私有字段,这就等于把封装打开了一个口子。原本设计者用 private 表达“这是内部实现,请不要依赖”,而反射让这个约定变得可以被无视。一旦代码依赖了某个类的私有成员,等这个类在升级中改了内部结构,依赖它的地方就会在运行时突然崩掉,而且编译期毫无提示。
第三是错误被推迟到了运行期。普通的方法调用写错了,编译器当场就会报错;而反射里的类名、方法名都是字符串,写错了编译器一无所知,只有真正执行到那一行才会抛出异常。这意味着很多问题要到测试甚至上线之后才暴露出来,排查成本也更高。
当然,也不能因此就否定反射。它最大的价值在于解耦:代码在编写时不需要知道具体的类型,只根据运行时的信息去处理,这种通用性是无法用普通调用替代的。像注解处理、序列化、依赖注入这些场景,如果没有反射,就只能靠大量重复的样板代码去实现。
所以现在我对反射的态度是:它是一种能力,而不是一种日常写法。框架和通用工具这类需要处理未知类型的场景,用反射是合理的;而在业务代码里,如果能用接口和多态解决,就尽量不要把调用关系藏到字符串里。把反射留给真正需要它的地方,既保住了灵活性,也避开了它带来的那些代价。

浙公网安备 33010602011771号