假设我有以下课程:
public class SomeClass {
private final int num;
public SomeClass(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
当我执行this code设置num
字段时,一切正常:
SomeClass obj = new SomeClass(0);
final Field field = SomeClass.class.getDeclaredField("num");
field.setAccessible(true);
Field modField = Field.class.getDeclaredField("modifiers");
modField.setAccessible(true);
modField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(obj, 1);
System.out.println(obj.getNum()); // Prints 1 instead of the initial value 0.
但是,当我从SomeClass
中删除构造函数时,这不再起作用,println
语句将打印0。
任何人都可以解释这种行为吗?
答案 0 :(得分:2)
让我们看一下Field.set
方法的Java doc:
如果基础字段是final,则该方法抛出一个 除非setAccessible(true)成功,否则IllegalAccessException 此Field对象和字段是非静态的。设置最终字段 这种方式只有在反序列化时才有意义 之前重建类的实例,空白的最终字段 它们可供程序的其他部分访问。用于 任何其他情境可能具有不可预测的影响,包括案件 程序的其他部分继续使用原始值 这个领域。
这意味着在您的示例中,如果删除构造函数,则需要将最终字段初始化为某个值,从而使其不为空。在这种情况下,如果您使用反射更改最终字段,则可以使用不可预测的效果,包括案例 程序的其他部分继续使用原始值 这个领域。
答案 1 :(得分:1)
首先请注意,即使字段为public
,您也会获得相同的行为,在这种情况下,您不需要设置field
可访问的内容:
final Field field = SomeClass.class.getDeclaredField("num");
Field modField = Field.class.getDeclaredField("modifiers");
modField.setAccessible(true);
modField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(obj, 1);
System.out.println(obj.num);
这是Java编译器优化的结果:实际字段num
设置为1
,但getNum
忽略该值,因为编译器认为它是' s final
。
即使删除了构造函数(demo),此行也会打印1
:
System.out.println(field.get(obj));
Java编译器注意到final int num
从未在其初始化程序之外分配,并将return num
替换为返回num
的初始值。
注意:您的实验提供了一个很好的理由,说明为什么不应该尝试修改您声明不可修改的字段。