Skip to content

反射平时零散接触过——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

五、反射的代价

反射很强大,但有三个代价:

  1. 性能差:反射调用比直接调用慢(要查元数据、安全检查),虽然后来有优化,仍比普通调用慢
  2. 破坏封装setAccessible(true) 绕过 private,能访问"不该访问"的(单例就是被反射破坏的,见《单例模式》)
  3. 编译期检查失效:反射的错误要到运行时才暴露

所以反射是"框架作者的工具",业务代码尽量少用——框架帮你用反射搞定通用逻辑,你写业务时直接写类型安全代码。

小结

  • 反射 = 运行时操作类,入口是 Class 对象(类的元数据)
  • 四大操作:拿蓝图(forName)→ 造实例(newInstance)→ 玩字段(get/set)→ 调方法(invoke)
  • 是 Spring IoC、动态代理、ORM、JSON 的基石
  • 代价:性能、破坏封装、编译期检查失效

想了解反射的一个经典应用,看《Java 动态代理:JDK 代理 vs CGLIB》;想了解反射怎么被用来破坏单例,看《单例模式》。