深入理解Java反射(超详细)

深入理解Java反射(超详细) 个人主页:一条泥憨鱼(欢迎各位大佬莅临)精选专栏:数据结构与算法JavaSE ,苍穹外卖日记AI学习​​​​​什么是反射通俗理解我们先想一个生活中的例子你手里有一个神秘的黑盒子不知道里面装了什么。正常情况下你要先看说明书源码才知道它有哪些按钮、怎么用。但如果你有一台X光机不用看说明书直接对着黑盒子扫一下就能看清楚里面有哪些零件、每个零件叫什么名字、怎么操作——这台X光机就是 Java 的反射机制。// 官方定义Java 反射机制是指在程序运行时Runtime对于任意一个类都能知道这个类的所有属性和方法对于任意一个对象都能调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为 Java 的反射机制。关键词是运行时。普通代码在编译时就确定了用哪个类、调哪个方法。而反射可以在程序跑起来之后才决定要操作哪个类——这带来了极大的灵活性是 Spring、MyBatis 等主流框架的底层核心。记忆技巧正常写代码编译期就确定类和方法 → 安全、快速使用反射运行期才确定类和方法 → 灵活、强大获取 Class 对象的三种方式使用反射的第一步是拿到目标类的Class对象。你可以把Class对象理解为那台X光机扫描后得到的类的说明书里面记录了该类的一切信息。Java 提供了三种方式来获取Class对象方式一类名.class最简单直接已知类名时使用编译期就确定不会抛出异常。ClassString clazz String.class; System.out.println(clazz); // 输出: class java.lang.String方式二对象.getClass()已有对象时使用当你手里已经有一个对象实例可以通过它获取对应的Class。String str Hello, 反射!; Class? clazz str.getClass(); System.out.println(clazz); // 输出: class java.lang.String方式三Class.forName(全类名)最灵活框架常用通过完整类名字符串动态加载类。类名可以来自配置文件、数据库等是框架最常用的方式。注意需要处理ClassNotFoundException。Javatry { // 全类名 包名 类名 Class? clazz Class.forName(java.lang.String); System.out.println(clazz); // 输出: class java.lang.String } catch (ClassNotFoundException e) { e.printStackTrace(); }重要提示同一个类无论用哪种方式获取Class对象得到的都是同一个对象JVM 中每个类只有一份 Class 对象。三种方式的结果完全相同String.class str.getClass() Class.forName(java.lang.String)利用反射获取构造方法拿到 Class 对象后我们就可以解剖这个类了。先来看如何获取构造方法Constructor。首先我们定义一个示例类Student后续所有例子都基于它Java · Student.java示例类public class Student { // 成员变量 public String name; private int age; // 无参构造 public Student() {} // 有参构造 public Student(String name, int age) { this.name name; this.age age; } // 成员方法 public void study() { System.out.println(name 正在学习Java); } private void sleep() { System.out.println(name 在睡觉...); } // getter/setter 省略... }常用的获取构造方法 API方法说明getConstructors()获取所有public构造方法getDeclaredConstructors()获取所有构造方法包括私有getConstructor(参数类型...)获取指定的 public 构造方法getDeclaredConstructor(参数类型...)获取指定的任意构造方法获取并使用构造方法import java.lang.reflect.Constructor; Class? clazz Class.forName(Student); // 1. 获取所有 public 构造方法 Constructor[] constructors clazz.getConstructors(); for (Constructor c : constructors) { System.out.println(c); } // 2. 获取有参构造并创建对象 Constructor? con clazz.getConstructor(String.class, int.class); Object stu con.newInstance(张三, 18); // 等价于 new Student(张三, 18) System.out.println(stu); // 3. 使用无参构造创建对象简便写法 Object stu2 clazz.getDeclaredConstructor().newInstance(); System.out.println(stu2);小贴士con.newInstance(参数...)就是反射版的new关键字。参数类型和顺序必须与构造方法一致。利用反射获取成员变量反射不仅能创建对象还能直接读写对象的成员变量Field即使是private修饰的私有变量也能访问方法说明getFields()获取所有public成员变量getDeclaredFields()获取所有成员变量包括私有getField(变量名)获取指定 public 变量getDeclaredField(变量名)获取指定任意变量获取并操作成员变量import java.lang.reflect.Field; Class? clazz Student.class; // 先创建一个 Student 对象用于测试 Object stu clazz.getDeclaredConstructor(String.class, int.class) .newInstance(张三, 18); // 1. 获取 public 变量 name 并修改 Field nameField clazz.getField(name); nameField.set(stu, 李四); // 修改 name 值 System.out.println(nameField.get(stu)); // 读取 name 值 → 李四 // 2. 获取 private 变量 age // ⚠️ 访问私有变量必须先调用 setAccessible(true) 暴力破解 Field ageField clazz.getDeclaredField(age); ageField.setAccessible(true); // 取消私有访问限制关键 ageField.set(stu, 20); // 修改 age 值 System.out.println(ageField.get(stu)); // 读取 age 值 → 20⚠️ 重点setAccessible(true)访问private成员时必须先调用setAccessible(true)否则会抛出IllegalAccessException。这个操作相当于强制解锁私有权限在框架中非常常见但在业务代码中要谨慎使用。利用反射获取成员方法反射还可以获取并调用任意方法Method包括私有方法甚至可以在不知道方法名的情况下通过字符串来动态调用。方法说明getMethods()获取所有 public 方法含父类继承的getDeclaredMethods()获取本类所有方法不含继承含私有getMethod(方法名, 参数类型...)获取指定 public 方法getDeclaredMethod(方法名, 参数类型...)获取本类指定任意方法获取并调用成员方法import java.lang.reflect.Method; Class? clazz Student.class; Object stu clazz.getDeclaredConstructor(String.class, int.class) .newInstance(张三, 18); // 1. 调用无参 public 方法 study() Method studyMethod clazz.getMethod(study); studyMethod.invoke(stu); // 输出张三 正在学习Java // 2. 调用带参方法假设有 setName(String name) 方法 Method setNameMethod clazz.getMethod(setName, String.class); setNameMethod.invoke(stu, 王五); // 等价于 stu.setName(王五) // 3. 调用 private 方法 sleep() Method sleepMethod clazz.getDeclaredMethod(sleep); sleepMethod.setAccessible(true); // 私有方法同样需要暴力破解 sleepMethod.invoke(stu); // 输出王五 在睡觉... // 4. 获取方法的返回值 Method getAgeMethod clazz.getMethod(getAge); Object result getAgeMethod.invoke(stu); System.out.println(年龄: result); // 年龄: 18method.invoke()参数说明method.invoke(obj, arg1, arg2, ...)第一个参数要调用方法的对象实例后续参数方法所需的实际参数值返回值方法的返回值如果是 void则返回 null反射的实际作用与应用场景你可能会问我直接new一个对象不就好了为什么要用这么麻烦的反射反射的真正价值在于当你不知道要用哪个类的时候。来看一个简单的框架模拟案例案例模拟框架——根据配置文件动态创建对象假设我们有一个配置文件config.properties# 只需修改这里程序行为就会改变 classNamecom.example.Student methodNamestudy使用反射读取配置并动态执行import java.util.Properties; import java.io.InputStream; import java.lang.reflect.*; public class ReflectDemo { public static void main(String[] args) throws Exception { // 1. 读取配置文件 Properties prop new Properties(); InputStream is ReflectDemo.class.getClassLoader() .getResourceAsStream(config.properties); prop.load(is); // 2. 获取类名和方法名 String className prop.getProperty(className); String methodName prop.getProperty(methodName); // 3. 使用反射动态创建对象并调用方法 // ✅ 不修改 Java 代码只改配置文件就能切换行为 Class? clazz Class.forName(className); Object obj clazz.getDeclaredConstructor().newInstance(); Method method clazz.getMethod(methodName); method.invoke(obj); } }现在把配置文件里的className改成com.example.Teacher程序就会操作 Teacher 类完全不需要修改 Java 代码重新编译。这正是 Spring 框架依赖注入IoC的基本原理反射在主流框架中的应用Spring IoC读取 XML / 注解配置 → 用反射创建 Bean 对象并注入依赖MyBatis读取 SQL 结果集 → 用反射把列值赋给 Java 对象的字段Jackson / GsonJSON 反序列化 → 用反射把 JSON 字段映射到 Java 对象JUnit扫描带 Test 注解的方法 → 用反射自动执行测试方法反射的优缺点总结✅ 优点灵活性强运行时动态操作类无需在编译期确定类型是框架设计的基石。✅ 优点降低耦合通过配置文件驱动代码与具体类解耦修改配置即可切换实现。⚠️ 缺点性能开销反射涉及动态解析比直接调用慢频繁调用场景应缓存 Method / Field 对象。⚠️ 缺点安全风险setAccessible(true)可绕过访问控制破坏封装性需谨慎使用。一张图回顾全部知识点Java 反射 ├── 获取 Class 对象 │ ├── 类名.class ← 编译期最安全 │ ├── 对象.getClass() ← 有对象实例时用 │ └── Class.forName(全类名) ← 运行时最灵活框架常用 │ ├── 获取构造方法 (Constructor) │ ├── getConstructors() ← 所有 public 构造 │ ├── getDeclaredConstructors() ← 所有构造含 private │ └── con.newInstance(参数) ← 创建对象 │ ├── 获取成员变量 (Field) │ ├── getFields() ← 所有 public 字段 │ ├── getDeclaredFields() ← 所有字段含 private │ ├── field.get(obj) ← 读取值 │ ├── field.set(obj, val) ← 修改值 │ └── setAccessible(true) ← 访问 private 必须调用 │ ├── 获取成员方法 (Method) │ ├── getMethods() ← 所有 public 方法含继承 │ ├── getDeclaredMethods() ← 本类所有方法含 private │ ├── method.invoke(obj, args...) ← 调用方法 │ └── setAccessible(true) ← 调用 private 必须调用 │ └── 应用场景 ├── Spring IoC 依赖注入 ├── MyBatis ORM 映射 ├── JSON 序列化 / 反序列化 └── 单元测试框架JUnit反射是高阶武器初学阶段不需要在业务代码中大量使用。但理解反射是读懂 Spring、MyBatis 等框架源码的前提。掌握本文的内容你已经具备了探索主流框架底层原理的基础能力今天的学习就暂时告一段落啦如果文章对您有用的话还请留下一个免费的小心心和关注哦祝您工作顺利生活愉快。我们下期再见