访问字段
约 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.grade
2.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)
。
需要注意的是,通过反射读写字段是一种非常规方法,它会破坏对象的封装。在实际开发中,应该谨慎使用反射,避免滥用。