
一、什么是反射Reflection反射机制程序在运行期动态获取类的信息、操作类 / 对象的属性、方法、构造器。正常编码编译期我们提前知道类名直接new 对象()调用方法代码在编译阶段就确定要操作哪个类。User user new User(); user.setName(张三); // 编译时就确定调用 User.setName反射运行期编译期不知道类是谁运行时拿到类的 Class 对象反向解析出这个类有什么构造方法、成员变量、成员方法然后动态创建对象、读写字段、调用方法。核心入口java.lang.Class每一个类加载后在 JVM 里都会生成唯一的Class对象代表这个类型。获取 Class 对象 3 种方式// 方式1类名.class 不会触发类的静态代码块执行 ClassUser clazz1 User.class; // 方式2对象.getClass() 必须已有实例 User u new User(); Class? extends User clazz2 u.getClass(); // 方式3Class.forName(全类名) 最常用会加载类、执行静态代码块 Class? clazz3 Class.forName(com.demo.User);二、反射能做什么核心 API都在java.lang.reflect包Constructor构造器用来 new 对象Field成员变量读取 / 修改属性Method成员方法调用方法Modifier修饰符工具判断 public/private/static 等重点反射可以突破访问权限setAccessible (true)可以访问 private 方法 / 属性这也是反射的双刃剑。三、完整示例代码先准备实体类 Userpackage com.demo; public class User { // 私有字段 private String name; public Integer age; // 无参构造 public User(){} // 有参构造 public User(String name, Integer age) { this.name name; this.age age; } // 私有方法 private String getSecret() { return 我是私有秘密; } // public方法 public void sayHello(String msg) { System.out.println(hello: msg); } // getter/setter public String getName() { return name; } public void setName(String name) { this.name name; } }示例 1反射获取构造器创建对象import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { // 1. 获取Class对象 Class? clazz Class.forName(com.demo.User); // 获取无参构造器创建实例 Constructor? noArgCon clazz.getDeclaredConstructor(); User user1 (User) noArgCon.newInstance(); System.out.println(user1); // 获取有参构造器 Constructor? argCon clazz.getDeclaredConstructor(String.class, Integer.class); User user2 (User) argCon.newInstance(李四, 20); System.out.println(user2.getName() , user2.age); } }getConstructor()只能拿 public 构造getDeclaredConstructor()拿到所有修饰符的构造包括 private。示例 2反射读写成员变量包含私有属性Class? clazz Class.forName(com.demo.User); User user (User) clazz.getDeclaredConstructor().newInstance(); // 获取私有字段 name Field nameField clazz.getDeclaredField(name); nameField.setAccessible(true); // 关闭权限检查允许访问private nameField.set(user, 王五); // 设置值 String nameVal (String) nameField.get(user); // 获取值 System.out.println(nameVal);示例 3反射调用方法public /privateClass? clazz Class.forName(com.demo.User); User user (User) clazz.getDeclaredConstructor().newInstance(); // 调用public方法 sayHello参数类型String Method sayHelloMethod clazz.getDeclaredMethod(sayHello, String.class); sayHelloMethod.invoke(user, 反射调用方法); // 调用私有方法 getSecret无参数 Method secretMethod clazz.getDeclaredMethod(getSecret); secretMethod.setAccessible(true); String result (String) secretMethod.invoke(user); System.out.println(result);method.invoke(对象实例, 参数列表)如果调用 static 方法第一个参数传null。四、反射优缺点✅ 优点高度灵活运行期动态创建对象、调用方法不用编译期硬编码类。框架的基石。解耦配置文件写类全名程序读取配置动态加载类。❌ 缺点性能低绕过编译优化需要做大量权限校验、类型解析频繁反射会有开销。破坏封装setAccessible(true)可以访问私有变量 / 私有方法违背面向对象封装。编译期失去类型检查类型错误只有运行时才抛出异常不易排查。安全问题安全管理器下会被限制很多安全扫描会标记反射。五、真实业务场景示例重点场景 1框架底层Spring IOC 容器Spring 读取 xml / 注解里的 bean 全类名使用反射实例化对象、注入属性。Spring 为什么要用反射程序员写的 Bean 类Spring 在写框架代码的时候根本不知道类名只能运行时动态创建实例。伪代码模拟 Spring 创建 BeanString beanClassName com.demo.User; // 从配置读取 Class? clazz Class.forName(beanClassName); Object bean clazz.getDeclaredConstructor().newInstance(); // 反射setter注入属性 Method setName clazz.getDeclaredMethod(setName, String.class); setName.invoke(bean, Spring注入名字);场景 2ORM 框架 MyBatis数据库查询结果 ResultSet反射实例化实体类反射给实体字段赋值。数据库表字段 - 实体属性MyBatis 不知道你写的 POJO只能反射 setter 或者直接给 field 赋值。场景 3单元测试 JUnitJUnit 扫描Test注解运行时反射找到所有标记 Test 的方法自动执行。场景 4通用工具对象拷贝工具BeanUtils比如 Spring BeanUtils、Hutool BeanUtil。输入 source 对象target 对象反射遍历所有字段把同名字段的值复制过去不用手写一堆 get/set。 伪代码public static void copy(Object source, Object target) throws Exception { Class? sourceCls source.getClass(); Class? targetCls target.getClass(); Field[] fields sourceCls.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); Object val f.get(source); Field targetField targetCls.getDeclaredField(f.getName()); targetField.setAccessible(true); targetField.set(target, val); } }场景 5RPC 框架Dubbo服务消费者拿到接口动态代理。底层使用反射调用远程接口方法。场景 6序列化工具Jackson / FastJSON序列化对象转 JSON反射扫描对象所有字段读取字段值转 json 反序列化 JSON 转对象反射创建实例给字段赋值。六、补充反射 动态代理延伸反射常和动态代理配合AOP 底层就是 JDK 动态代理依赖反射。JDK 动态代理只能代理接口运行期生成代理类通过反射调用目标方法在前后增加切面逻辑日志、事务。七、扩展问题getClass()和.class和Class.forName()区别特性obj.getClass()类名.classClass.forName (全类名)语法实例方法必须有对象类字面量无需实例静态方法传入字符串全类名多态✅ 返回运行时真实类型❌ 固定就是这个类❌ 字符串指定哪个类就是哪个类初始化一定已初始化不触发静态代码块默认触发静态代码块可关闭类型检查运行期编译期校验运行期才校验类不存在抛异常使用场景拿到对象真实类型、反射取实例信息反射提前拿到 Class、工具类不想执行静态块根据字符串动态加载类JDBC、SPI 框架getConstructorvsgetDeclaredConstructorgetConstructor只拿 public 构造getDeclaredConstructor获取本类全部构造不限访问权限。setAccessible(true)作用是否可以永久修改访问权限只是关闭本次反射访问检查不会修改源码里的访问修饰符。反射性能差怎么优化使用MethodHandle、缓存 Constructor/Method 对象不要循环内反复获取 Class、Method。反射能不能获取泛型真实类型可以通过ParameterizedType获取这就是 MyBatis 解析泛型 ListUser的原理。