W
e
l
c
o
m
e
: )

Java反射

一、反射是什么

反射(Reflection) 是Java赋予程序在运行时动态“自省”和“操作”类的能力。打个比方:

常规编码是“编剧”写死剧本(new User()),而反射是“导演”在开拍现场拿着剧本(Class对象),现场决定用哪个演员、调哪句台词,甚至临时修改台词。

二、反射能干什么

通过 java.lang.Classjava.lang.reflect 包下的API,你能做到:

  1. “窥探”类的全貌 :在运行时获取类的构造器、方法、字段、注解、父类、实现的接口。
  2. “凭空”创建对象 :调用 Class.forName("xxx").newInstance() 或通过构造器动态创建实例(不需要写 new 关键字)。
  3. “调用”任意方法 :调用对象上的任何方法,包括私有方法(配合 setAccessible(true) 打破封装)。
  4. “修改”任意属性 :给对象字段赋值,即使是 private static final 理论上也能强行改写(虽然极不推荐)。

三、反射有哪些应用场景

1. 主流框架的“灵魂”——IOC(控制反转)和依赖注入

Spring在启动时,会扫描带有 @Component@Service 的类。

  • 通过反射获取类的 无参构造器 ,动态创建Bean实例。
  • 扫描类中带有 @Autowired 的字段,通过反射调用 field.set(bean, dependency) 把依赖注入进去,而不用你手动写 set 方法。

2. JSON/XML 序列化与反序列化(Jackson / Gson / Fastjson)

当你写 gson.fromJson(jsonStr, User.class) 时:

  • 底层通过反射获取 User 类的所有字段名和类型 (如 String name)。
  • 遍历JSON键值对,用反射调用 field.set(obj, jsonValue),把数据填进对象的私有字段里。它根本不需要你提供 setter 方法也能赋值(通过 setAccessible(true))。

3. JDBC 与 ORM 框架(MyBatis / Hibernate)

MyBatis 将数据库的 ResultSet(结果集)映射到你的 POJO 时:

  • 读取 User 类的字段名(如 userName)。
  • ResultSet 中取 rs.getObject("user_name")
  • 通过反射 method.invoke(user, value) 调用 setUserName() 方法,将数据逐行注入。这就是为什么你只写个接口和XML,就能把数据库数据变出Java对象。

4. 动态代理(AOP 面向切面编程的核心)

Spring AOP(事务管理、日志监控)底层依赖动态代理。

  • 运行时通过反射生成目标类的 代理子类
  • 当你调用 userService.save() 时,代理类用反射拦截方法调用,先开启事务(invoke 前织入逻辑),再调用原方法(invoke 转发),最后提交事务(invoke 后织入逻辑)。

5. 开发工具与调试(IDEA 智能提示、JUnit 单元测试)

  • IDEA 能弹出 . 后的方法列表,是因为它用反射实时解析当前变量的 Class 信息。
  • JUnit 执行测试时,反射扫描所有带有 @Test 注解的方法,并动态调用它们。你的 main 方法本质上也是JVM用反射加载启动类的。

四、反射具体该怎么使用

// 这个类没有实现任何接口,包含各种访问修饰符
public class Student {
    // 私有字段 + 静态字段
    private String name;
    private int age;
    public static String school = "北京大学";

    // 无参构造(public)
    public Student() {
        System.out.println("调用了无参构造");
    }

    // 有参构造(private)
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("调用了私有有参构造:" + name + ", " + age);
    }

    // 公有方法
    public void publicMethod(String msg) {
        System.out.println("公有方法执行,消息:" + msg + ",name=" + name);
    }

    // 私有方法(带返回值)
    private String privateMethod(int x) {
        System.out.println("私有方法执行,入参:" + x);
        return "私有方法返回:" + (x * 10);
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // -------- 1. 获取 Class 对象 ----------
        Class<?> clazz = Class.forName("com.hzp.pk4.Student");
        System.out.println("类名:" + clazz.getName());

        // -------- 2. 造对象(调用构造器) ----------
        // 2.1 调用无参构造(最常用)
        Student student = (Student) clazz.getDeclaredConstructor().newInstance();
        System.out.println("反射创建的对象1:" + student);

        // 2.2 调用私有有参构造(模拟框架反序列化时强行创建对象)
        Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class, int.class);
        privateConstructor.setAccessible(true); // 暴力破解私有权限!
        Student student2 = (Student) privateConstructor.newInstance("张三", 18);
        System.out.println("反射创建的对象2:" + student2);

        // -------- 3. 调方法 ----------
        // 3.1 调用公有方法
        Method publicMethod = clazz.getMethod("publicMethod", String.class);
        publicMethod.invoke(student, "Hello Reflect"); // 第一个参数是实例,后面是实参

        // 3.2 调用私有方法(并且获取返回值)
        Method privateMethod = clazz.getDeclaredMethod("privateMethod", int.class);
        privateMethod.setAccessible(true); // 必须破解,否则报 IllegalAccessException
        String result = (String) privateMethod.invoke(student, 5);
        System.out.println("私有方法返回值:" + result);

        // -------- 4. 改属性(修改私有字段) ----------
        // 4.1 修改私有实例字段 name
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(student, "李四"); // 直接把 student 的 name 改为 "李四"
        System.out.println("修改后的 student:" + student);

        // 4.2 修改私有实例字段 age
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.setInt(student, 25); // 用 setInt 避免装箱开销
        System.out.println("修改年龄后的 student:" + student);

        // 4.3 修改公有静态字段 school(静态字段传 null 作为对象实例)
        Field schoolField = clazz.getField("school"); // 公有字段用 getField,不用 getDeclaredField
        schoolField.set(null, "清华大学"); // 静态传 null
        System.out.println("修改后的学校:" + Student.school);
    }
}
posted @ 2026-08-02 15:23  寒月静无光  阅读(2)  评论(0)    收藏  举报