使用反射覆盖最终静态字段是否有限制? [英] Is there a limit to overriding final static field with Reflection?

查看:86
本文介绍了使用反射覆盖最终静态字段是否有限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been faced in some of my Unit test with a strange behaviour with Reflection on final static field. Below is an example illustrating my issue.

我有一个包含整数的基本单例类

I have a basic Singleton class that holds an 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;
    }

}

我的测试用例循环并通过反射将 VALUE 设置为迭代索引,然后断言 VALUE 正确地等于迭代索引.

My test case is looping and setting through Reflection the VALUE to the iteration index and then asserting that the VALUE is rightfully equal to the iteration index.

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);
        }
    }

}

结果非常令人惊讶,因为它不是恒定的,我的测试在大约 1000 次迭代时失败,但似乎永远不会相同.

The result is quite surprising because it's not constant, my test fails around iteration ~1000 but it never seems to be always the same.

有人遇到过这个问题吗?

Anyone has already faced this issue ?

推荐答案

JLS 提到在构造后修改 final 字段是有问题的 - 见17.5.最终字段语义

The JLS mentions that modifying final fields after construction is problematic - see 17.5. final Field Semantics

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

Fields declared final are initialized once, but never changed under normal circumstances. The detailed semantics of final fields are somewhat different from those of normal fields. In particular, compilers have a great deal of freedom to move reads of final fields across synchronization barriers and calls to arbitrary or unknown methods. Correspondingly, compilers are allowed to keep the value of a final field cached in a register and not reload it from memory in situations where a non-final field would have to be reloaded.

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

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

Another problem is that the specification allows aggressive optimization of final fields. Within a thread, it is permissible to reorder reads of a final field with those modifications of a final field that do not take place in the constructor.

除此之外,Field.set 的 JavaDocs 也包含关于此的警告:

In addition to that, the JavaDocs of Field.set also include a warning about this:

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

Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field.

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

It seems that what we are witnessing here is the JIT taking advantage of the reordering and caching possibilities granted by the Language Specification.

这篇关于使用反射覆盖最终静态字段是否有限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆