Java反射核心是运行时动态操作类,获取Class对象有三种方式:类名.class(编译期已知、最安全)、对象.getClass()(获实际运行时类型)、Class.forName()(动态加载、触发类初始化);调用私有成员需setAccessible(true)绕过访问检查;反射性能低因查找耗时、安全校验及泛型擦除补偿;框架如Spring、MyBatis依赖反射实现依赖注入与ORM映射。

Java 反射不是考死记硬背,而是考你是否真正理解“运行时动态操作类”这个核心逻辑。面试官问反射题,本质是在验证你对 Java 类加载、封装边界、框架底层的把握程度。答得好,不靠堆代码,靠讲清动机、路径和代价。
怎么获取 Class 对象?三种方式的区别必须说清
这是几乎所有反射题的起点,不能只罗列写法,要说明适用场景和隐含风险:
- 类名.class:编译期已知类型,最安全、最快;但无法用于字符串配置(比如从 properties 文件读类名)
- 对象.getClass():适合已有实例、需判断真实运行时类型(注意它返回的是实际类,不是声明类型)
- Class.forName("全限定名"):唯一支持动态加载的方案,会触发类的初始化(执行 static 块),若类不存在或初始化失败会抛 ClassNotFoundException 或 ExceptionInInitializerError
怎么调用私有方法/访问私有字段?关键不止是 setAccessible(true)
很多人背下 setAccessible 就以为通关了,其实面试官想听的是“为什么需要它”和“用了之后会发生什么”:
- setAccessible(true) 是绕过 JVM 的访问控制检查,不是“删除 private”,而是临时关闭校验
- 它只对当前 Field/Method/Constructor 生效,不影响其他反射操作
- 在安全管理器(SecurityManager)启用的环境里,该调用可能被拒绝(虽然现在基本不用了)
- 修改 final 字段时,若该字段是编译期常量(如 static final String s = "abc"),JVM 已做内联优化,反射改了也看不到效果
反射为什么慢?哪些操作开销最大?
性能问题是高频追问点,光说“因为要查表、做检查”不够,得指出具体瓶颈:
立即学习“Java免费学习笔记(深入)”;
- 查找过程耗时:getDeclaredMethod() 需遍历所有方法并匹配签名,比直接调用多出几十倍耗时
- 安全检查开销:每次 invoke 或 get/set 都会做访问权限校验(除非提前 setAccessible(true))
- 类型擦除补偿:泛型信息在运行时丢失,反射需额外解析字节码才能还原参数化类型(如获取 List<String> 的泛型实参)
- 建议:高频调用场景应缓存 Method/Field 对象,避免重复查找
反射在框架里到底干了什么?举一个真实例子
脱离框架谈反射是空谈。Spring 和 MyBatis 的典型用法就是最好的答案素材:
- Spring IOC:根据 @Autowired 注解扫描字段,用反射 setAccessible(true) 给 private 成员注入依赖
- MyBatis ResultSetHandler:通过反射调用 setter 方法,把数据库结果映射到实体类,不管字段是 public 还是 private
- JSON 库(如 Jackson):序列化时遍历所有 get 方法或字段,反序列化时用无参构造 + 反射设值,支撑 POJO 零配置
不复杂但容易忽略:反射不是万能钥匙,它是打破封装的“紧急通道”。用得好提升扩展性,滥用就带来可维护性下降和潜在安全风险。真正懂反射的人,答得清楚“什么时候该用、为什么能用、用了要付出什么代价”。


















