反射成员
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(", "));
}
}要点回顾
| 操作 | Field | Method | Constructor |
|---|---|---|---|
| 获取值 | field.get(obj) | — | — |
| 设置值 | field.set(obj, val) | — | — |
| 调用方法 | — | method.invoke(obj, args) | — |
| 创建实例 | — | — | ctor.newInstance(args) |
| 私有成员 | 需要 setAccessible(true) | 需要 setAccessible(true) | 需要 setAccessible(true) |
Field、Method、Constructor 是反射的三板斧。记住它们的模式:获取 → 设置可访问 → 操作。
