一、上午 3h:反射入门 + 获取 Class 字节码对象

1. 反射概念与作用(0.5h)

什么是反射?

反射:Java 程序运行时,可以动态获取任意类的完整结构(构造方法、成员变量、成员方法),并且可以动态创建对象、调用方法、操作属性的机制。

核心特点
  1. 动态性:不用提前写死代码,运行时才确定操作哪个类、哪个方法
  2. 破封装:可以访问private私有成员(普通代码无法直接访问)
  3. 框架基石:Spring、MyBatis、SpringBoot 底层全是反射,没有反射就没有主流 Java 框架
反射适用场景
  • 框架底层(自动注入、对象创建)
  • 配置文件解析(通过字符串全类名创建对象)
  • 动态代理、单元测试
  • 通用工具类开发

2. 类的生命周期与 Class 对象(0.5h)

类的生命周期

.java 源文件 → 编译 → .class 字节码文件 → JVM 加载 → 生成唯一 Class 对象 → 创建实例对象

Class 对象是什么?
  • 每个类被加载到 JVM 后,只会生成一个唯一的 Class 字节码对象
  • Class 对象中封装了当前类的所有信息:包名、类名、父类、接口、构造、变量、方法
  • 反射的所有操作,都必须从 Class 对象开始

3. 获取 Class 类三种方式(重点必背 2h)

三种方式(必须背熟)
  1. 类名.class:编译期确定,最简单
  2. 对象.getClass ():通过已有对象获取
  3. Class.forName ("全类名"):运行时动态加载,框架最常用

完整代码示例 + 逐行解释

我们先创建一个实体类Student,后续所有反射案例都用这个类:

java

运行

// 实体类:用于反射测试
public class Student {
    // 私有成员变量
    private String name;
    public int age;

    // 无参构造
    public Student() {}

    // 有参构造
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 公有方法
    public void study() {
        System.out.println("学生正在学习");
    }

    // 私有方法
    private void sleep() {
        System.out.println("学生正在睡觉");
    }

    // getter/setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}

获取Class对象的完整代码:

java

运行

public class GetClassObject {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:类名.class  编译期确定,最常用
        Class<Student> c1 = Student.class;
        System.out.println("方式1:" + c1);

        // 方式2:对象.getClass()  必须先有对象
        Student student = new Student();
        Class<? extends Student> c2 = student.getClass();
        System.out.println("方式2:" + c2);

        // 方式3:Class.forName("全类名")  运行时动态加载,框架核心
        Class<?> c3 = Class.forName("com.example.Student"); // 包名+类名
        System.out.println("方式3:" + c3);

        // 重点:三个对象是同一个!证明一个类只有一个Class对象
        System.out.println(c1 == c2); // true
        System.out.println(c1 == c3); // true
    }
}
代码解释
  • c1:直接通过类获取,最安全、性能最高
  • c2:必须先创建对象,才能获取 Class,不适合框架
  • c3最常用,传入字符串全类名,运行时加载类,Spring/MyBatis 都用它
  • 三个对象地址相同,证明一个类只有一个 Class 对象

二、下午 2.5h:反射获取类成员并操作

1. 反射获取构造方法(0.8h)

核心 API
  • getConstructor(Class...):获取公有构造方法
  • getDeclaredConstructor(Class...):获取任意构造(含私有)
  • newInstance():通过构造创建对象
  • setAccessible(true)暴力反射,破封装

完整代码示例

java

运行

import java.lang.reflect.Constructor;

public class ReflectConstructor {
    public static void main(String[] args) throws Exception {
        // 1. 获取Class对象
        Class<?> clazz = Class.forName("com.example.Student");

        // ====================== 1. 获取无参公有构造 ======================
        Constructor<?> con1 = clazz.getConstructor();
        Object obj1 = con1.newInstance(); // 创建对象
        System.out.println("无参构造创建:" + obj1);

        // ====================== 2. 获取有参公有构造 ======================
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        Object obj2 = con2.newInstance("张三", 20);
        System.out.println("有参构造创建:" + obj2);

        // ====================== 3. 暴力反射获取私有构造(补充) ======================
        // 假如Student有private Student(String name){}
        // Constructor<?> con3 = clazz.getDeclaredConstructor(String.class);
        // con3.setAccessible(true); // 破封装
        // Object obj3 = con3.newInstance("李四");
    }
}
核心知识点
  • 无参构造是框架创建对象的默认方式
  • setAccessible(true):关闭访问检查,允许访问私有成员
  • 反射创建对象的本质:不使用new关键字创建对象

2. 反射获取成员变量(0.8h)

核心 API
  • getField(String name):获取公有成员变量
  • getDeclaredField(String name):获取任意变量(含私有)
  • set(Object obj, value):给对象赋值
  • get(Object obj):获取对象的值
  • setAccessible(true):破私有

完整代码示例

java

运行

import java.lang.reflect.Field;

public class ReflectField {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.example.Student");
        Object obj = clazz.newInstance(); // 先创建对象

        // ====================== 1. 操作公有变量 age ======================
        Field ageField = clazz.getField("age");
        ageField.set(obj, 18); // 赋值
        int age = (int) ageField.get(obj); // 取值
        System.out.println("公有age:" + age);

        // ====================== 2. 操作私有变量 name ======================
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true); // 破封装
        nameField.set(obj, "反射赋值"); // 给私有变量赋值
        String name = (String) nameField.get(obj); // 获取私有变量值
        System.out.println("私有name:" + name);

        System.out.println("最终对象:" + obj);
    }
}
代码解释
  • 公有变量直接操作
  • 私有变量必须:getDeclaredField + setAccessible(true)
  • 反射可以操作任何权限的变量,这是普通 Java 代码做不到的

3. 反射获取成员方法(0.9h)

核心 API
  • getMethod(String name, Class...):获取公有方法
  • getDeclaredMethod(String name, Class...):获取任意方法(含私有)
  • invoke(Object obj, args...)调用方法
  • 返回值:方法执行结果

完整代码示例

java

运行

import java.lang.reflect.Method;

public class ReflectMethod {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.example.Student");
        Object obj = clazz.newInstance();

        // ====================== 1. 调用公有方法 study() ======================
        Method studyMethod = clazz.getMethod("study");
        studyMethod.invoke(obj); // 执行方法

        // ====================== 2. 调用私有方法 sleep() ======================
        Method sleepMethod = clazz.getDeclaredMethod("sleep");
        sleepMethod.setAccessible(true); // 破封装
        sleepMethod.invoke(obj); // 执行私有方法

        // ====================== 3. 调用带参有返回值方法(补充) ======================
        Method setNameMethod = clazz.getMethod("setName", String.class);
        setNameMethod.invoke(obj, "王五");

        Method getNameMethod = clazz.getMethod("getName");
        String name = (String) getNameMethod.invoke(obj);
        System.out.println("getName返回值:" + name);
    }
}
核心重点
  • invoke() 是反射调用方法的核心
  • 私有方法必须开启暴力访问
  • 支持带参、带返回值的方法调用

三、晚上 1.5h:综合实操 + 复盘总结

必做练习(完整综合案例)

java

运行

// 反射综合实操:一行代码完成 创建对象 + 赋值 + 调用方法
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        // 1. 获取Class
        Class<?> clazz = Class.forName("com.example.Student");

        // 2. 创建对象
        Object obj = clazz.newInstance();

        // 3. 赋值私有变量
        java.lang.reflect.Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(obj, "综合练习");

        // 4. 赋值公有变量
        java.lang.reflect.Field ageField = clazz.getField("age");
        ageField.set(obj, 22);

        // 5. 调用方法
        java.lang.reflect.Method studyMethod = clazz.getMethod("study");
        studyMethod.invoke(obj);

        // 6. 输出结果
        System.out.println(obj);
    }
}

核心知识点总结

  1. 反射入口Class对象(三种获取方式)
  2. 三大成员操作
    • 构造:Constructor → 创建对象
    • 变量:Field → 赋值 / 取值
    • 方法:Method → 调用执行
  3. 暴力反射setAccessible(true) 必须掌握
  4. 核心价值:动态、解耦、框架底层核心

重要补充:原计划遗漏的必备知识点

我帮你补充了面试高频、框架必用但计划里没写的关键内容:

1. 反射的优缺点

  • 优点:动态、解耦、灵活性极高、框架必备
  • 缺点:性能比普通代码低、破坏封装、安全性降低

2. Class.forName 会执行类的静态代码块

java

运行

Class.forName("com.example.Student"); // 会执行static代码块
  • JDBC 连接数据库就是用这个特性

3. 反射获取父类、接口、注解

java

运行

// 获取父类
Class superClass = clazz.getSuperclass();
// 获取接口
Class[] interfaces = clazz.getInterfaces();
// 获取注解
Annotation[] annotations = clazz.getAnnotations();

4. 反射破坏单例模式

  • 单例模式构造私有,但反射可以暴力调用私有构造,创建多个对象
  • 这是高级面试高频考点

5. 数组、枚举、基本类型的 Class 对象

java

运行

Class<int[]> arrClass = int[].class;
Class<Season> enumClass = Season.class;
Class<Integer> intClass = int.class;

Day29 验收标准(完整版)

你完成今天学习后,必须达到:

  1. 能说出反射是什么、作用、场景
  2. 默写三种获取 Class 对象的方式
  3. 会用反射:
    • 创建对象
    • 操作公有 / 私有变量
    • 调用公有 / 私有方法
  4. 会写暴力反射代码
  5. 能独立完成反射操作实体类综合案例
  6. 知道反射是所有 Java 框架的底层原理

总结

这份内容完全覆盖你的学习计划,并做了深度拓展

  • 所有知识点都有详细解释
  • 所有案例都有完整代码 + 逐行注释
  • 补充了计划遗漏的高频重点(静态代码块、父类 / 注解、破坏单例、数组 Class 等)
  • 层次清晰:上午入门→下午实操→晚上综合练习
  • 完全贴合 Java 学习节奏,可直接跟着练习、面试、框架学习

更多推荐