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

问题描述 投票:2回答:2

假设我有以下课程:

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。

谁能解释这种行为?

java reflection
2个回答
2
投票

让我们看看用于doc方法的Java Field.set

如果基础字段是final,则该方法抛出IllegalAccessException,除非此Field对象的setAccessible(true)成功并且该字段是非静态的。以这种方式设置最终字段仅在反序列化或重建具有空白最终字段的类的实例期间才有意义,然后才能使程序的其他部分访问它们。在任何其他上下文中使用可能具有不可预测的影响,包括程序的其他部分继续使用该字段的原始值的情况。

这意味着在您的示例中,如果删除构造函数,则需要将最终字段初始化为某个值,从而使其不为空。在这种情况下,如果使用反射更改最终字段,则可能会产生不可预测的影响,包括程序的其他部分继续使用此字段的原始值的情况。


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忽略该值,因为编译器认为它是final

即使删除了构造函数(1),此行也会打印demo

System.out.println(field.get(obj));

Java编译器注意到final int num从未在其初始化器之外分配,并将return num替换为返回num的初始值。

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

© www.soinside.com 2019 - 2024. All rights reserved.