Skip to content

反射入门(Class、Method、Field)

概述

反射(Reflection) 是 Java 在运行时检查或修改类、方法、字段等程序行为的能力。通过反射,可以在编译期未知具体类型的情况下,根据类名加载类、创建实例、调用方法、读写字段。许多框架(如 Spring、JUnit、序列化库)都依赖反射实现「配置驱动」或「约定优于配置」;学习反射有助于理解这类框架的工作原理。

前置建议

已掌握 类与对象封装与访问修饰符继承,以及 注解 的基本概念。


反射的入口:Class 对象

在 Java 中,每个已加载的类在 JVM 里都有且仅有一个 Class 对象,它是反射的入口。要反射操作某个类,首先需要获取它的 Class 实例。

获取 Class 的三种常见方式

方式适用场景示例
类名.class编译期已知类型String.classUser.class
对象.getClass()已有对象实例"hello".getClass()
Class.forName("全限定类名")类名来自配置或字符串Class.forName("java.util.ArrayList")
java
// 1. 类名.class —— 编译期已知类型
Class<String> stringClass = String.class;

// 2. 对象.getClass() —— 已有实例
String s = "hello";
Class<?> c1 = s.getClass();  // 与 String.class 是同一 Class 实例

// 3. Class.forName —— 类名是字符串(可能来自配置文件等)
Class<?> c2 = Class.forName("java.util.ArrayList");

注意

Class.forName("全限定类名") 会抛出受检异常 ClassNotFoundException,必须用 try-catch 或 throws 处理。若类不存在或未在类路径中,会在此处报错。


通过 Class 获取构造方法、方法与字段

拿到 Class<?> 后,可以获取该类的构造方法方法字段,再在运行时调用或访问。

常用 API 一览

  • 构造方法getConstructor(参数类型...)getDeclaredConstructor(...)newInstance(...)
  • 方法getMethod("方法名", 参数类型...)getDeclaredMethod(...)method.invoke(对象, 实参...)
  • 字段getField("字段名")getDeclaredField(...)field.get(对象)field.set(对象, 值)

Declared 的方法会返回本类声明的成员(包括 private),但不包括继承的;不带 Declared 的只返回 public 成员(包括继承的)。


基本用法

1. 获取并调用构造方法创建实例

通过 getDeclaredConstructor(参数类型...) 获取构造方法,再调用 newInstance(实参...) 创建对象。

java
import java.lang.reflect.Constructor;

public class ReflectionConstructDemo {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("java.util.ArrayList");
        // 获取无参构造方法
        Constructor<?> cons = clazz.getDeclaredConstructor();
        Object list = cons.newInstance();
        System.out.println(list);  // []
    }
}

若构造方法带参数,需传入对应的 Class 数组:

java
// 例如获取 String 的 byte[] 构造方法
Constructor<String> cons = String.class.getDeclaredConstructor(byte[].class);
String s = cons.newInstance(new byte[]{72, 101, 108, 108, 111});

2. 获取并调用方法(Method)

通过 getMethod("方法名", 参数类型...)getDeclaredMethod(...) 得到 Method,再通过 method.invoke(对象, 实参...) 调用。静态方法传入的「对象」可为 null

java
import java.lang.reflect.Method;

public class ReflectionMethodDemo {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("java.util.ArrayList");
        Object list = clazz.getDeclaredConstructor().newInstance();

        // 获取 add(Object) 方法并调用
        Method add = clazz.getMethod("add", Object.class);
        add.invoke(list, "A");
        add.invoke(list, "B");
        System.out.println(list);  // [A, B]

        // 获取 size() 方法并调用(无参)
        Method size = clazz.getMethod("size");
        int n = (Integer) size.invoke(list);
        System.out.println(n);    // 2
    }
}

3. 获取并读写字段(Field)

通过 getField("字段名")getDeclaredField("字段名") 得到 Field私有字段默认不可访问,需要先 field.setAccessible(true)(会受模块系统与安全管理器约束)。读写使用 field.get(对象)field.set(对象, 值)

java
import java.lang.reflect.Field;

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

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

// 在其它类中通过反射读写 User 的私有字段
public class ReflectionFieldDemo {
    public static void main(String[] args) throws Exception {
        User user = new User("张三", 20);
        Class<?> clazz = user.getClass();

        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);   // 允许访问 private
        String name = (String) nameField.get(user);
        System.out.println(name);       // 张三

        nameField.set(user, "李四");
        System.out.println(nameField.get(user));  // 李四
    }
}

提示

setAccessible(true) 会破坏封装,仅在对测试、框架或兼容旧代码等场景下合理使用;生产代码中应优先通过正常 API 访问。


使用示例

示例 1:根据类名创建实例并调用无参方法

适合「类名来自配置」的简单工厂场景。

java
public static Object createAndCall(String className, String methodName) throws Exception {
    Class<?> clazz = Class.forName(className);
    Object obj = clazz.getDeclaredConstructor().newInstance();
    Method method = clazz.getMethod(methodName);
    return method.invoke(obj);
}

示例 2:列出类的所有公开方法

便于调试或简单工具。

java
public static void printPublicMethods(Class<?> clazz) {
    for (Method m : clazz.getMethods()) {
        System.out.println(m.getReturnType().getSimpleName() + " " + m.getName());
    }
}

示例 3:通过反射调用私有方法(慎用)

测试或特殊兼容场景下,可能需要对私有方法做可访问性设置后再调用。

java
Method privateMethod = clazz.getDeclaredMethod("internalHelper", String.class);
privateMethod.setAccessible(true);
Object result = privateMethod.invoke(instance, "arg");

注意事项

注意

  • 性能:反射调用比直接调用慢,且不易被 JIT 优化。高频路径上应避免用反射。
  • 封装setAccessible(true) 会绕过访问检查,破坏封装,仅在有充分理由时使用。
  • 类型安全:反射在编译期无法做类型检查,invokegetset 的返回值与参数多为 Object,需要强转,易在运行时出现 ClassCastException

注意

使用 Class.forNamegetMethod/getDeclaredMethod 时,若类名或方法签名写错,会在运行时抛出 ClassNotFoundExceptionNoSuchMethodException 等,需做好异常处理或校验配置。


相关链接

基于 VitePress 构建