获取和设置字段值
给定一个类的实例,可以使用反射来设置该类中字段的值。通常仅在特殊情况下无法以常规方式设置值时才执行此操作。因为这样的访问通常会违反该类的设计意图,所以应绝对谨慎地使用它。
Book类说明如何设置 long,array 和 enum 字段类型的值。 Field中描述了获取和设置其他原始类型的方法。
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;
enum Tweedle { DEE, DUM }
public class Book {
public long chapters = 0;
public String[] characters = { "Alice", "White Rabbit" };
public Tweedle twin = Tweedle.DEE;
public static void main(String... args) {
Book book = new Book();
String fmt = "%6S: %-12s = %s%n";
try {
Class<?> c = book.getClass();
Field chap = c.getDeclaredField("chapters");
out.format(fmt, "before", "chapters", book.chapters);
chap.setLong(book, 12);
out.format(fmt, "after", "chapters", chap.getLong(book));
Field chars = c.getDeclaredField("characters");
out.format(fmt, "before", "characters",
Arrays.asList(book.characters));
String[] newChars = { "Queen", "King" };
chars.set(book, newChars);
out.format(fmt, "after", "characters",
Arrays.asList(book.characters));
Field t = c.getDeclaredField("twin");
out.format(fmt, "before", "twin", book.twin);
t.set(book, Tweedle.DUM);
out.format(fmt, "after", "twin", t.get(book));
// production code should handle these exceptions more gracefully
} catch (NoSuchFieldException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
}
这是相应的输出:
$ java Book
BEFORE: chapters = 0
AFTER: chapters = 12
BEFORE: characters = [Alice, White Rabbit]
AFTER: characters = [Queen, King]
BEFORE: twin = DEE
AFTER: twin = DUM
Note:
通过反射设置字段的值具有一定的性能开销,因为必须进行各种操作,例如验证访问权限。从运行时的角度来看,效果是相同的,并且操作是原子的,就好像直接在类代码中更改了值一样。
使用反射可能会导致某些运行时优化丢失。例如,以下代码很可能由 Java 虚拟机进行优化:
int x = 1;
x = 2;
x = 3;
使用Field.set*()
的等效代码可能不会。