JavaSE 进阶:反射核心知识点(Class 对象 + 反射操作 + 实战案例)

79 阅读7分钟

大家好!反射是 Java 中核心技术之一——它能让程序在运行时“看透”类的内部结构,甚至操作私有方法和属性,这在框架开发、配置化编程中至关重要。

这篇文章会,先讲清类加载与反射的关联,再拆解反射的核心操作,最后结合实战案例,帮你彻底吃透反射的核心知识点,应对面试和开发场景都游刃有余。

一、认识反射

1.1 什么是反射?

反射(Reflection)是 Java 提供的一种能力:程序在运行时,能够获取类的完整结构(包括类名、属性、方法、构造器等),并动态操作这些结构。简单说,就是“运行时探知类的一切”。

正常编程:比如 new User() 创建对象,编译期就知道要调用 User 类;

反射:即使编译时不知道类名,只要有类的全名,就能动态创建对象、调用方法。

1.2 反射的作用

  • 可以得到一个类的全部成员然后操作。
  • 可以破坏封装性。
  • 可以绕过泛型的约束

1.3 反射的缺点

反射虽强,但有明显缺点,需避免滥用:

  • 性能略差:跳过编译检查,直接操作类结构,比正常调用慢 10-100 倍
  • 破坏封装:能直接操作私有属性和方法,违背面向对象的封装原则;

二、反射第一步:获取Class对象

反射的核心是“操作 Class 对象”——每个类在 JVM 中只会生成一个 Class 对象,它包含了类的所有信息。 Class 对象就是是反射的“入口”——所有反射操作,都必须先获取目标类的 Class 对象。

2 获取 Class 对象的 3 种核心方式

  • Class c1 = 类名.class
  • 调用Class提供方法:public static Class forName(String package);
  • Object提供的方法: public Class getClass(); Class c3 = 对象.getClass();
    public static void main(String[] args) throws Exception {
        //目标:反射第一步获取class对象

        // Class c1 = 类名.class
        Class clazz = Demo01.class;
        System.out.println(clazz);

        //调用Class提供方法: public static Class forName(String package);
        clazz = Class.forName("com.tgt._02reflection._01getClass.Demo01");
        System.out.println(clazz);

        // Object提供的方法:: public Class getClassO;Class c3 = 对象.getClass();
        Demo01 demo01 = new Demo01();
        clazz = demo01.getClass();
        System.out.println(clazz);
    }

适用场景对比

  • 方式1:已知对象实例,简单但灵活性低;
  • 方式2:编译期确定类,常用于泛型或静态场景;
  • 方式3:编译期无需知道类,通过字符串动态获取(如从配置文件读取类名),是框架的核心用法。

三、反射核心操作

获取 Class 对象后,就能通过反射操作类的构造器、属性、方法——这是反射的核心落地能力。

3.1 操作构造器:动态创建对象

通过反射调用构造器创建对象,支持无参和有参构造,解决“编译期无法 new 对象”的问题。

代码实现

先创建一个Dog类,里面封装着属性、方法、构造器等

public class Dog {
    private String name;
    private int age;

    public void eat(){
        System.out.println("狗吃东西...");
    }

    private String eat(String name){
        System.out.println("狗吃" + name);
        return "汪汪汪!";
    }

    public Dog() {
    }

    private Dog(String name){
        this.name = name;
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

通过反射调用构造器创建对象

import java.lang.reflect.Constructor;
import java.util.Arrays;

public class Demo01 {
    public static void main(String[] args) throws Exception {
        //目标:使用反射方式动态获取指定类的构造器方法
        //作用:可以通过反射灵活执行构造器创建对象

        //1.获取狗的字节码对象
        Class clazz = Dog.class;

        //2.获取所有构造器方法对象
        Constructor[] constructors = clazz.getDeclaredConstructors();
        System.out.println("打印狗类中所有构造器方法:");
        Arrays.stream(constructors).forEach(System.out::println);

        //3.获取指定一个无参构造器 public Dog() 并调用执行创建对象
        Constructor constructor = clazz.getConstructor();
        Object obj1 = constructor.newInstance();
        System.out.println("获取指定一个无参构造器 public Dog() 并调用执行创建对象:"+obj1);//Dog{name=null,age=0}

        //4.获取指定一个私有  private Dog(String name)构造器并调用执行创建对象
        Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
        constructor1.setAccessible(true);
        Object obj2 = constructor1.newInstance("旺财");
        System.out.println("获取指定一个私有  private Dog(String name)构造器并调用执行创建对象:"+obj2);////Dog{name='旺财',age=0}


        //封装一个方法,传入任何类型都可以创建无参构造器对象返回(包含私有无参构造器)
        //创建狗对象
        Object obj3 = createObjectByClass(Dog.class);
        Object obj4 = createObjectByClass(Object.class);
        System.out.println(obj3);////Dog{name=null,age=0}
        System.out.println(obj4);//打印内存地址
    }

    /**
     * 通用方法:只需要传入指定class和参数,就可以创建任何对象
     * @param clazz
     * @return
     */
    public static Object createObjectByClass(Class clazz){
        try {
            Constructor constructor = clazz.getConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

3.2 操作属性:读写公有/私有属性

反射能突破封装,操作类的私有属性,但需谨慎使用(破坏封装)。

代码实现

引用之前的Dog类

import java.lang.reflect.Field;
import java.util.Arrays;

public class Demo02 {
    public static void main(String[] args) throws Exception {
        //目标:使用反射操作成员变量

        //1.获取所有成员变量并打印
        Class clazz = Dog.class;
        Field[] fields = clazz.getDeclaredFields();
        Arrays.stream(fields).forEach(System.out::println);

        //2.获取private String name成员变量对象
        Field field = clazz.getDeclaredField("name");

        //3.给name成员变量赋值
        //面向对象方式实现: 对象名.成员变量名=值
        //3.1 获取对象,可以利用无参构造器创建对象
        Object obj = clazz.getConstructor().newInstance();

        //3.2 暴力反射
        field.setAccessible(true);

        //3.3 赋值
        field.set(obj, "旺财");

        //4.获取name成员变量的值
        //面向对象方式实现: 对象名.成员变量名
        Object o = field.get(obj);
        System.out.println("name的值=" + o);

    }
}

注意:操作私有属性/方法前,必须调用 setAccessible(true) 取消访问检查,否则抛 IllegalAccessException。

3.3 操作方法:调用公有/私有方法

反射调用方法时,需指定方法名和参数类型(避免方法重载冲突)。

代码实现

引用之前的Dog类

import java.lang.reflect.Method;
import java.util.Arrays;

public class Demo03 {
    public static void main(String[] args) throws Exception {
        //目标:使用反射操作成员方法

        //1.获取所有成员方法并打印
        Class clazz = Dog.class;
        Method[] methods = clazz.getDeclaredMethods();
        Arrays.stream(methods).forEach(System.out::println);

        //2.获取public void eat()成员方法对象并执行
        Method eat = clazz.getMethod("eat");
        //面向对象执行方法: 对象名.方法名(),所以需要创建对象
        Object o = clazz.getConstructor().newInstance();
        eat.invoke(o);

        //3.获取私有成员方法private String eat(String name) 并执行
        Method eat1 = clazz.getDeclaredMethod("eat", String.class);
        Object o2 = clazz.getConstructor().newInstance();
        eat1.setAccessible(true);
        Object o3 = eat1.invoke(o2,"骨头");
        System.out.println(o3);

    }
}

四、反射实战案例:简易配置化对象生成器

使用反射做一个简易版的框架

5.1 需求说明

对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。

5.2 实现步骤

  1. 编写反射工具类:将任意对象数据写入磁盘文件
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.lang.reflect.Field;

public class ObjectDataWriteFileUtil {
    /**
     * 将任意对象数据写入磁盘文件
     * @param object
     */
    public static void writeObjectToFile(Object object){
        try(//1.创建字符输出流操作day05-02/data.txt,采用追加输出数据
            BufferedWriter writer = new BufferedWriter(new FileWriter("Reflection/data.txt",true))
        ) {

            //2.获取obj的字节码对象Class
            Class clazz = object.getClass();

            //3.获取所有成员变量对象Field[]
            Field[] fields = clazz.getDeclaredFields();

            //4.输出“==============类名=============”
            writer.write(String.format("==================%s==================%n",clazz.getSimpleName()));

            //5.遍历Field[]得到每个成员变量的名称与值
            for (Field field : fields) {
                //6.将名称与值写入输出到文件中
                writer.write(field.getName());
                field.setAccessible(true);
                Object o = field.get(object);
                writer.write("=");
                writer.write(String.valueOf(o));
                writer.newLine();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        System.out.printf("当前对象%s写入磁盘成功!%n",object);
    }
}

2.编写Student类和Teacher类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {

    private String name;
    private double salary;
}

3.编写测试类

public class Demo01 {
    public static void main(String[] args) {
        //准备学生对象
        Student student = new Student("张三", 20, '男', 1.8, "敲代码");
        //准备老师对象
        Teacher t = new Teacher("tgt", 1000000);

        //调用反射工具类写入磁盘
        ObjectDataWriteFileUtil.writeObjectToFile(student);
        ObjectDataWriteFileUtil.writeObjectToFile(t);
    }
}

五、总结与延伸

7.1 核心总结

  • 反射入口:Class 对象( 3 种获取方式);
  • 核心操作:获取并操作构造器、属性、方法;
  • 关键能力:动态操作类的全部结构,是框架的核心;
  • 使用原则:按需使用,避免滥用(性能差、破坏封装),高频场景缓存反射结果。