使用Reflection覆盖最终静态字段是否有限制?

Sam*_*ssi 14 java reflection

我在一些单元测试中遇到了一个奇怪的行为反射最终静态场.以下是说明我的问题的示例.

我有一个基本的Singleton类,它包含一个Integer

public class BasicHolder {
    private static BasicHolder instance = new BasicHolder();

    public static BasicHolder getInstance() {
        return instance;
    }

    private BasicHolder() {
    }

    private final static Integer VALUE = new Integer(0);

    public Integer getVALUE() {
        return VALUE;
    }

}
Run Code Online (Sandbox Code Playgroud)

我的测试用例是循环并设置Reflection the VALUE到迭代索引,然后声明VALUE正确地等于迭代索引.

class TestStaticLimits {
    private static final Integer NB_ITERATION = 10_000;

    @Test
    void testStaticLimit() {

        for (Integer i = 0; i < NB_ITERATION; i++) {
            setStaticFieldValue(BasicHolder.class, "VALUE", i);
            Assertions.assertEquals(i, BasicHolder.getInstance().getVALUE(), "REFLECTION DID NOT WORK for iteration "+i);
            System.out.println("iter " + i + " ok" );

        }
    }

    private static void setStaticFieldValue(final Class obj, final String fieldName, final Object fieldValue) {
        try {
            final Field field = obj.getDeclaredField(fieldName);
            field.setAccessible(true);
            final Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            field.set(null, fieldValue);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException("Error while setting field [" + fieldName + "] on object " + obj + " Message " + e.getMessage(), e);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

结果是非常令人惊讶的,因为它不是常数,我的测试在迭代〜1000时失败但它似乎永远不会一样.

任何人都已经遇到过这个问题?

Hul*_*ulk 10

JLS提到在施工后修改最终油田是有问题的 - 见 17.5.最终的场语义学

声明为final的字段初始化一次,但在正常情况下从未更改过.最终字段的详细语义与普通字段的语义略有不同.特别是,编译器有很大的自由来跨越同步障碍移动最终字段的读取,并调用任意或未知的方法.相应地,允许编译器将最终字段的值保存在寄存器中,而不是在必须重新加载非最终字段的情况下从内存重新加载它.

17.5.3.最终字段的后续修改:

另一个问题是规范允许对最终字段进行积极优化.在一个线程中,允许使用构造函数中不发生的最终字段的那些修改来重新排序最终字段的读取.

除此之外,Field.setJavaDocs还包含一个警告:

以这种方式设置最终字段仅在反序列化或重建具有空白最终字段的类的实例期间才有意义,然后才能使程序的其他部分访问它们.在任何其他上下文中使用可能具有不可预测的影响,包括程序的其他部分继续使用该字段的原始值的情况.

我们在这里看到的似乎是JIT利用语言规范授予的重新排序和缓存可能性.