删除ctor后,通过反射设置私有最终字段失败

时间:2017-12-26 19:45:46

标签: java reflection

假设我有以下课程:

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。

任何人都可以解释这种行为吗?

2 个答案:

答案 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的初始值。

注意:您的实验提供了一个很好的理由,说明为什么不应该尝试修改您声明不可修改的字段。