java反射是运行时动态获取类信息并操作成员的机制,本质是将编译期绑定推迟至运行期,依托jvm加载生成的唯一class对象实现;它支撑spring ioc实例化、mybatis结果映射等核心功能,但需权衡性能与安全。

Java 反射机制在面试中常以“连环炮”形式出现——问题层层递进,从定义到原理,再到实操、性能、安全与框架落地。核心不在于背答案,而在于讲清逻辑链条:为什么需要反射 → 它靠什么工作 → 怎么用才对 → 用的时候踩过哪些坑。
一、反射是什么?为什么不是“查文档式复述”
不能只说“运行时获取类信息、调用方法、访问字段”。要带出对比和动机:
- Java 是静态编译语言,正常调用(如
User user = new User())所有类型、方法、字段都必须在编译期确定; - 反射把“类结构检查”从编译期推迟到运行期,比如读取配置文件里的类名字符串
"com.example.ServiceImpl",再加载、实例化、调用——这叫动态编程; - 本质是程序在运行时“照镜子”,通过 JVM 加载后生成的
Class对象,反向解析自身结构。
二、Class 对象怎么来?三种方式的区别必须讲透
这是连环问的起点,后续所有操作都依赖它:
-
MyClass.class:最常用,编译期已知类,零异常、性能最优,适合框架内部硬编码场景(如 Spring 的@Bean方法里); -
obj.getClass():适用于已有对象但类型不确定(如泛型擦除后的List>元素),返回的是实际运行时类型; -
Class.forName("全限定名"):唯一能触发类初始化的方式(会执行 static 块),常用于配置驱动(如 JDBC 加载com.mysql.cj.jdbc.Driver),但可能抛ClassNotFoundException。
追问常考:ClassLoader.loadClass() 和 forName() 区别?前者不初始化类,后者默认初始化。
三、私有成员真能访问吗?setAccessible() 不是“万能钥匙”
很多候选人答“加 setAccessible(true) 就行”,但面试官会立刻追问细节:
- 可以访问 private 字段/方法,前提是安全管理器(SecurityManager)未禁止;
- 修改
final字段:能设值,但若该字段被 JVM 编译期优化为常量(如static final String s = "abc"),修改后其他代码仍读到原内联值; - 访问 private 构造器创建对象:可行,但若类明确禁止(如枚举、单例且构造器抛异常),反射也绕不过业务逻辑约束。
四、为什么反射慢?怎么优化?不能只说“因为要校验”
要落到 JVM 层面和实际手段:
- 慢的主因:每次反射调用都要做访问权限检查、参数类型匹配、方法查找(非虚方法表直接跳转)、取消 JIT 内联优化;
- 优化不是“少用”,而是“缓存 + 预热”:缓存
Method或Field对象(避免重复getDeclaredMethod()),配合MethodHandle或VarHandle替代高频反射调用; - Spring 等框架实际做法:启动时批量反射解析,生成代理或字节码(如 CGLIB),运行时走原生调用路径。
五、没有反射,Spring IOC 和 MyBatis 还能活吗?
这是检验是否真懂框架底层的关键一问:
- Spring IOC:靠反射完成 Bean 实例化(包括 private 构造器)、依赖注入(set 方法或字段赋值)、生命周期回调(
@PostConstruct方法查找与调用); - MyBatis:SQL 执行后,用反射将 ResultSet 每行映射为 Java 对象(匹配字段名→
Field.set()),同时处理@Results注解中的属性绑定逻辑; - 注意点:现代框架已大量用 ASM、Byte Buddy 等生成字节码替代纯反射,但反射仍是元数据发现和初始适配的不可替代环节。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











