1. 深夜的共鸣:代码为何“写死”了?

深夜 11 点,你正在为一个插件化系统发愁。由于需求变动,你不得不根据配置文件动态加载不同的业务类。

// 如果需求增加,你得不停地写 if-else
if (type.equals(“A”)) return new ServiceA();
else if (type.equals(“B”)) return new ServiceB();

看着屏幕上几十个 if-else,你满头大汗:难道每次新增功能都要改源码、重新编译、重新发布吗?

就在这时,你想到了那个被称为“Java 之魂”的技术——反射

2. 什么是反射?(定义与直观感受)

反射是指在程序运行状态中:

对于任意一个类,都能够知道这个类的所有属性和方法。

对于任意一个对象,都能够调用它的任意一个方法和属性。

核心结论: 反射赋予了 Java 程序“自省”的能力,让代码从“静态编译”走向“动态运行”。

静态加载 vs 动态加载
在这里插入图片描述

3. 反射的底层原理

反射之所以能生效,是因为 JVM 在加载类时,会产生一个 Class 对象。
在这里插入图片描述
当程序运行时,JVM 会将 .class 文件读入内存,并为之创建一个 java.lang.Class 实例。反射 API 就是通过这个 Class 实例来反向获取类的元数据信息的。

4. 反射的“四大金刚”

在反射的世界里,你只需要掌握这四个核心类:

Class:类的实体,反射的入口。

Field:类的成员变量。

Method:类的方法。

Constructor:类的构造函数。

实战代码示例

// 1. 获取 Class 对象
Class<?> clazz = Class.forName(“com.example.UserService”);

// 2. 暴力反射:获取私有构造并创建对象
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true); // 突破访问权限!
Object service = constructor.newInstance();

// 3. 调用私有方法
Method method = clazz.getDeclaredMethod(“privateMethod”, String.class);
method.setAccessible(true);
method.invoke(service, “CSDN 爆款博主”);

⚠️ 避坑指南: > * 永远不要忘记 setAccessible(true),否则调用私有成员会抛出 IllegalAccessException。

Class.forName() 必须捕获 ClassNotFoundException

5. 面试加分项:底层逻辑深度剖析

Q:为什么反射会慢?慢在哪里?

A:这是大厂面试的必考题。反射慢主要在三个地方:

类型检查:反射调用方法时,JVM 需要频繁检查参数类型、权限等,无法在编译期优化。

元数据查找:getMethod 等操作需要遍历类的所有方法列表,涉及字符串匹配。

JIT 优化失效:由于是动态调用,JIT (即时编译器) 很难对反射代码进行内联优化。

Q:反射如何破坏单例模式?

A: 即使是私有构造函数,反射也可以通过 setAccessible(true) 强行调用,从而创建多个实例。解决办法是在构造函数里增加判断:if (instance != null) throw new RuntimeException(“单例已被破坏!”); 或者使用 枚举 。

6. 总结:反射是一把双刃剑

反射是 Spring、MyBatis、Hibernate 等各大框架的基石。没有反射,就没有 AOP,没有 IOC,更没有如今繁荣的 Java 生态。

但请记住: 在普通的业务逻辑中,应尽量避免过度使用反射,以维持代码的可读性和性能平衡。

📥 互动环节

每日一思: 既然反射可以访问私有属性,那 Java 的封装性是不是名存实亡了?你是如何看待这个“设计矛盾”的?欢迎在评论区留言!

如果你觉得这篇博文帮到了你,请务必:
✅ 点赞
(让我知道你来过)
🌟 关注(不定期分享底层原理干货)
📂 收藏(面试前拿出来突击必过)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐