反射平时零散接触过——Spring 靠它注入、动态代理靠它调用方法、单例被它破坏,但"反射到底能干什么、怎么用"一直没系统梳理。这篇把反射从头讲清:Class 对象、四大核心 API、以及它为什么是框架的基石。
一、解决什么问题:运行时操作类
正常写代码,是"编译期就知道类长什么样"——写 new User() 时,User 的字段、方法在编译期就确定了。
反射反过来:运行时才拿到类的信息、操作类。它能做到:
- 运行时知道一个类有哪些字段、方法、构造器
- 运行时创建一个"编译期完全未知"的类的实例
- 访问私有字段、调用私有方法(突破封装)
框架之所以能"通用"(不用写死你的类),靠的就是反射——Spring 扫描到你的 @Service 类时,它不认识这个类,只能用反射去创建、注入。
二、核心:Class 对象(类的"元数据")
反射的入口是 Class 对象——每个类在 JVM 里都有一个对应的 Class 对象,存着这个类的全部信息(字段、方法、构造器),就像类的"蓝图"。
java
// 三种方式拿到 Class 对象
Class<?> clazz1 = User.class; // 编译期知道类
Class<?> clazz2 = obj.getClass(); // 通过实例拿
Class<?> clazz3 = Class.forName("com.example.User"); // 通过全类名字符串(运行时才知道类名)Class.forName("全类名") 最常用——框架从配置文件/注解里拿到类名字符串,就能反射出这个类,这就是"通用"的秘诀。
三、反射四大核心操作
拿到 Class 对象后,就能做四件事:
java
Class<?> clazz = Class.forName("com.example.User");
// ① 获取类信息
String name = clazz.getName(); // 类名
Field[] fields = clazz.getDeclaredFields(); // 所有字段
Method[] methods = clazz.getDeclaredMethods(); // 所有方法
// ② 创建实例
Object obj = clazz.getDeclaredConstructor().newInstance();
// ③ 访问字段(含私有)
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 突破 private
String value = (String) nameField.get(obj); // 读
nameField.set(obj, "BinMaker"); // 写
// ④ 调用方法(含私有)
Method method = clazz.getDeclaredMethod("sayHello");
method.setAccessible(true);
Object result = method.invoke(obj); // 调用 obj.sayHello()四句话记:Class.forName 拿蓝图 → newInstance 造实例 → get/set 玩字段 → invoke 调方法。
四、实际应用:框架的基石
反射不是用来炫技的,它是这些框架的地基:
| 框架 | 怎么用反射 |
|---|---|
| Spring IoC | 扫描 @Service,反射创建实例、注入依赖 |
| JDK 动态代理 | method.invoke() 调目标方法(见《Java 动态代理》) |
| ORM(MyBatis) | 把结果集映射到实体,靠反射 set 字段 |
| JSON 序列化 | 反射读字段,转成 JSON |
五、反射的代价
反射很强大,但有三个代价:
- 性能差:反射调用比直接调用慢(要查元数据、安全检查),虽然后来有优化,仍比普通调用慢
- 破坏封装:
setAccessible(true)绕过 private,能访问"不该访问"的(单例就是被反射破坏的,见《单例模式》) - 编译期检查失效:反射的错误要到运行时才暴露
所以反射是"框架作者的工具",业务代码尽量少用——框架帮你用反射搞定通用逻辑,你写业务时直接写类型安全代码。
小结
- 反射 = 运行时操作类,入口是
Class对象(类的元数据) - 四大操作:拿蓝图(forName)→ 造实例(newInstance)→ 玩字段(get/set)→ 调方法(invoke)
- 是 Spring IoC、动态代理、ORM、JSON 的基石
- 代价:性能、破坏封装、编译期检查失效
想了解反射的一个经典应用,看《Java 动态代理:JDK 代理 vs CGLIB》;想了解反射怎么被用来破坏单例,看《单例模式》。
