Skip to content

反射成员

Field、Method、Constructor——这三位是反射的核心成员。它们分别对应类的三个组成部分:数据(字段)、行为(方法)、创建(构造)。

理解它们,等于理解了反射操作 80% 的场景。

三者的共同祖先

Field、Method、Constructor 都实现了 java.lang.reflect.Member 接口。这个接口定义了访问成员的通用行为:

java
public interface Member {
    String getName();      // 成员名称
    int getModifiers();    // 修饰符
    Class<?> getDeclaringClass();  // 声明这个成员的类
}

为什么要有这个接口?因为框架作者可以用统一的方式处理不同类型的成员,而不需要关心具体是字段还是方法。

Field:数据的反射

Field 用来描述类的成员变量。它能读取值、写入值,还能获取类型信息。

读取和写入

java
public class FieldBasics {

    static class User {
        public String name = "张三";
        private int age = 25;
        public static String TAG = "USER";  // 静态字段
    }

    public static void main(String[] args) throws Exception {
        User user = new User();
        Class<?> clazz = user.getClass();

        // 读取 public 字段——简单直接
        Field nameField = clazz.getField("name");
        Object value = nameField.get(user);
        System.out.println("name = " + value);

        // 写入 public 字段
        nameField.set(user, "李四");
        System.out.println("name after set = " + nameField.get(user));

        // 读取 private 字段——需要 setAccessible
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);  // 不加这行 = IllegalAccessException
        int age = ageField.getInt(user);
        System.out.println("age = " + age);

        // 快捷方法:getInt/setInt 用于基本类型
        ageField.setInt(user, 30);
        System.out.println("age after setInt = " + ageField.getInt(user));

        // 读取静态字段——对象传 null
        Field tagField = clazz.getDeclaredField("TAG");
        tagField.setAccessible(true);
        System.out.println("TAG = " + tagField.get(null));
    }
}

为什么要用 setInt/getInt?

这涉及到 Java 的类型包装。对于基本类型,直接用 get()/set() 返回/接受的是 Object,JVM 需要自动装箱拆箱。而 getInt()/setInt() 直接操作原生类型,避免了额外的装箱开销:

java
public class FieldTypeDemo {

    public int count = 42;
    public boolean active = true;

    public static void main(String[] args) throws Exception {
        Class<?> clazz = FieldTypeDemo.class;
        Field countField = clazz.getDeclaredField("count");
        Field boolField = clazz.getDeclaredField("active");

        countField.setAccessible(true);
        boolField.setAccessible(true);

        // 方式一:Object 版本(需要强制类型转换)
        Object rawCount = countField.get(new FieldTypeDemo());
        int count = (Integer) rawCount;  // 装箱后再拆箱

        // 方式二:基本类型版本(更高效)
        int fastCount = countField.getInt(new FieldTypeDemo());
        boolean fastBool = boolField.getBoolean(new FieldTypeDemo());

        System.out.println("count = " + fastCount + ", active = " + fastBool);
    }
}

获取字段类型

java
public class FieldTypeInfo {

    public int intField;
    public String stringField;
    public List<String> listField;
    public int[] arrayField;

    public static void main(String[] args) throws Exception {
        Class<?> clazz = FieldTypeInfo.class;

        for (Field f : clazz.getDeclaredFields()) {
            Class<?> type = f.getType();
            System.out.println(f.getName() + " 的类型是: " + type.getSimpleName());

            // 检查是否是数组
            if (type.isArray()) {
                System.out.println("  → 数组类型,组件类型: " + type.getComponentType().getSimpleName());
            }

            // 检查是否是基本类型
            if (type.isPrimitive()) {
                System.out.println("  → 基本类型");
            }
        }
    }
}

Method:行为的反射

Method 用来描述类的方法。它的核心能力是 invoke()——动态调用任意方法。

调用实例方法

java
public class MethodInvokeDemo {

    static class Calculator {
        public int add(int a, int b) {
            return a + b;
        }

        public String greet(String name) {
            return "Hello, " + name + "!";
        }

        public <T> void print(T value) {
            System.out.println("value = " + value);
        }
    }

    public static void main(String[] args) throws Exception {
        Calculator calc = new Calculator();
        Class<?> clazz = calc.getClass();

        // 调用 add 方法
        Method addMethod = clazz.getDeclaredMethod("add", int.class, int.class);
        int result = (int) addMethod.invoke(calc, 10, 20);
        System.out.println("add(10, 20) = " + result);

        // 调用 greet 方法
        Method greetMethod = clazz.getDeclaredMethod("greet", String.class);
        String greeting = (String) greetMethod.invoke(calc, "World");
        System.out.println("greet = " + greeting);

        // 泛型方法 —— 泛型参数在运行时被擦除,实际参数类型是 Object
        Method printMethod = clazz.getDeclaredMethod("print", Object.class);
        printMethod.invoke(calc, 123);        // 传入 int,会自动装箱
        printMethod.invoke(calc, "hello");    // 传入 String
    }
}

调用静态方法

静态方法不属于任何实例,调用时第一个参数传 null

java
public class StaticMethodDemo {

    public static int square(int x) {
        return x * x;
    }

    public static String format(String template, Object... args) {
        return String.format(template, args);
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = StaticMethodDemo.class;

        // 调用静态方法 —— invoke 的第一个参数是 null
        Method squareMethod = clazz.getDeclaredMethod("square", int.class);
        int result = (int) squareMethod.invoke(null, 5);
        System.out.println("square(5) = " + result);

        // 调用可变参数方法 —— 数组形式
        Method formatMethod = clazz.getDeclaredMethod("format", String.class, Object[].class);
        String formatted = (String) formatMethod.invoke(null, "Name: %s, Age: %d", "张三", 25);
        System.out.println("format = " + formatted);
    }
}

方法的参数和返回值

java
public class MethodMetadata {

    public String process(int count, String name, List<?> data) throws IOException {
        return name + ": " + count;
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = MethodMetadata.class;
        Method method = clazz.getDeclaredMethod("process", int.class, String.class, List.class);

        // 方法名
        System.out.println("方法名: " + method.getName());

        // 返回类型
        System.out.println("返回类型: " + method.getReturnType().getSimpleName());

        // 参数类型
        System.out.println("参数类型:");
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            System.out.println("  参数" + i + ": " + paramTypes[i].getSimpleName());
        }

        // 异常类型
        System.out.println("声明的异常:");
        for (Class<?> ex : method.getExceptionTypes()) {
            System.out.println("  - " + ex.getSimpleName());
        }
    }
}

Constructor:构造的反射

Constructor 用来描述构造方法。创建对象有两种方式——通过 Constructor,或者通过 Class 本身。

java
public class ConstructorBasics {

    static class Person {
        private String name;
        private int age;

        public Person() {
            System.out.println("无参构造被调用");
        }

        public Person(String name) {
            this.name = name;
            System.out.println("单参构造被调用: " + name);
        }

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

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

    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;

        // 方式一:通过 Class.newInstance()(已废弃)
        // Person p1 = clazz.newInstance();  // JDK 9 已废弃

        // 方式二:通过 Constructor.newInstance()(推荐)
        // 调用无参构造
        Constructor<Person> noArg = clazz.getDeclaredConstructor();
        Person p1 = noArg.newInstance();
        System.out.println("p1 = " + p1);

        // 调用单参构造
        Constructor<Person> oneArg = clazz.getDeclaredConstructor(String.class);
        Person p2 = oneArg.newInstance("张三");
        System.out.println("p2 = " + p2);

        // 调用私有构造 —— 需要 setAccessible
        Constructor<Person> privateCtor = clazz.getDeclaredConstructor(String.class, int.class);
        privateCtor.setAccessible(true);
        Person p3 = privateCtor.newInstance("李四", 30);
        System.out.println("p3 = " + p3);
    }
}

遍历所有成员

有时候你想知道一个类有哪些字段、哪些方法、哪些构造方法:

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

public class AllMembersInspection {

    static class Sample {
        public String publicField;
        private int privateField;
        public static String TAG = "SAMPLE";

        public void publicMethod() {}
        private void privateMethod() {}

        public Sample() {}
        public Sample(String arg) {}
        private Sample(int arg) {}
    }

    public static void main(String[] args) {
        Class<Sample> clazz = Sample.class;

        System.out.println("=== 字段 ===");
        for (Field f : clazz.getDeclaredFields()) {
            String mod = Modifier.toString(f.getModifiers());
            System.out.println((mod.isEmpty() ? "" : mod + " ") +
                              f.getType().getSimpleName() + " " + f.getName());
        }

        System.out.println("\n=== 方法 ===");
        for (Method m : clazz.getDeclaredMethods()) {
            String mod = Modifier.toString(m.getModifiers());
            System.out.println((mod.isEmpty() ? "" : mod + " ") +
                              m.getReturnType().getSimpleName() + " " + m.getName() +
                              "(" + getParamNames(m) + ")");
        }

        System.out.println("\n=== 构造方法 ===");
        for (Constructor<?> c : clazz.getDeclaredConstructors()) {
            System.out.println(c.getName() + "(" + getParamNames(c) + ")");
        }
    }

    static String getParamNames(Executable e) {
        return Arrays.stream(e.getParameterTypes())
                     .map(Class::getSimpleName)
                     .collect(java.util.stream.Collectors.joining(", "));
    }
}

要点回顾

操作FieldMethodConstructor
获取值field.get(obj)
设置值field.set(obj, val)
调用方法method.invoke(obj, args)
创建实例ctor.newInstance(args)
私有成员需要 setAccessible(true)需要 setAccessible(true)需要 setAccessible(true)

Field、Method、Constructor 是反射的三板斧。记住它们的模式:获取 → 设置可访问 → 操作。

基于 VitePress 构建