访问字段
约 1217 字大约 4 分钟
2025-07-15
对 Java 中的任意 Object 实例,只要获取了它的 Class,就可以获取它的所有信息。本节主要介绍如何通过 Class 实例获取字段信息。
Class 类提供了以下几个方法来获取字段:
| 方法名 | 描述 |
|---|---|
getField(name) | 根据字段名获取某个 public 的 field(包括父类) |
getDeclaredField(name) | 根据字段名获取当前类的某个 field(不包括父类) |
getFields() | 获取所有 public 的 field(包括父类) |
getDeclaredFields() | 获取当前类的所有 field(不包括父类) |
下面是一个示例:
// reflection
public class Main {
public static void main(String[] args) throws Exception {
Class stdClass = Student.class;
// 获取public字段"score":
System.out.println(stdClass.getField("score"));
// 获取继承的public字段"name":
System.out.println(stdClass.getField("name"));
// 获取private字段"grade":
System.out.println(stdClass.getDeclaredField("grade"));
}
}
class Student extends Person {
public int score;
private int grade;
}
class Person {
public String name;
}这段代码展示了如何使用 getField() 和 getDeclaredField() 方法来获取 Student 类的字段。getField() 可以获取包括父类在内的公共字段,而 getDeclaredField() 只能获取当前类中声明的字段,不包括父类的字段。
程序输出的 Field 类似如下:
public int Student.score
public java.lang.String Person.name
private int Student.grade2.1 Field 对象
一个 Field 对象包含了字段的所有信息:
getName():返回字段名称,例如,"name"。getType():返回字段类型,也是一个Class实例,例如,String.class。getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
例如,对于 String 类的 value 字段:
public final class String {
private final byte[] value;
}可以使用反射获取该字段的信息:
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false上述代码演示了如何通过 Field 对象的 getName()、getType() 和 getModifiers() 方法获取字段的名称、类型和修饰符。其中,Modifier 类提供了一系列静态方法,用于判断修饰符的类型,如 isFinal()、isPublic()、isPrivate() 等。
2.2 获取字段值
在 Java 反射中,获取 Field 实例后,下一步是获取该字段对应的实例的值。例如,对于 Person 类的实例,可以先获取 name 字段的 Field 实例,然后获取该实例 name 字段的具体值。
以下代码展示了如何使用 Field.get(Object) 方法获取指定实例的字段值:
// reflection
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Object p = new Person("Xiao Ming");
Class c = p.getClass();
Field f = c.getDeclaredField("name");
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}代码解释:
- 首先创建了一个
Person类的实例p。 - 通过反射获取了
name字段的Field实例f。 - 使用
f.get(p)方法获取了实例p的name字段的值。
如果字段是 private 的,直接调用 f.get(p) 会抛出 IllegalAccessException 异常。要解决这个问题,需要在调用 f.get(p) 之前,先调用 f.setAccessible(true) 方法,允许访问该私有字段。
Field f = c.getDeclaredField("name");
f.setAccessible(true); // 允许访问 private 字段
Object value = f.get(p);尽管使用反射可以获取 private 字段的值,但类的封装仍然具有重要意义。在正常情况下,我们通过 p.name 来访问 Person 类的 name 字段时,编译器会根据 public、protected 和 private 访问修饰符来决定是否允许访问,从而保证了数据的封装性。反射是一种非常规的用法,主要用于工具或底层框架,以便在不知道目标实例任何信息的情况下,获取特定字段的值。
需要注意的是,setAccessible(true) 方法的调用可能会失败。如果 JVM 运行期存在 SecurityManager,它会根据预设的规则进行安全检查,可能会阻止 setAccessible(true) 的调用。例如,SecurityManager 可能不允许对以 java 和 javax 开头的 package 中的类调用 setAccessible(true),以保证 JVM 核心库的安全性。
2.3 设置字段值
通过 Field 实例可以获取指定实例的字段值,也可以设置字段的值。设置字段值是通过 Field.set(Object, Object) 实现的,其中第一个 Object 参数是指定的实例,第二个 Object 参数是待修改的值。示例代码如下:
// reflection
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Person p = new Person("Xiao Ming");
System.out.println(p.getName()); // "Xiao Ming"
Class c = p.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(p, "Xiao Hong");
System.out.println(p.getName()); // "Xiao Hong"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}上述代码展示了如何通过反射修改 Person 对象的 name 字段的值。同样的,修改非 public 字段,需要首先调用 setAccessible(true)。
需要注意的是,通过反射读写字段是一种非常规方法,它会破坏对象的封装。在实际开发中,应该谨慎使用反射,避免滥用。