java - 用反射覆盖最终静态字段是否有限制?

标签 java reflection

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

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

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 正确地等于迭代索引。

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 次左右失败,但它似乎永远不会始终相同。

有人遇到过这个问题吗?

最佳答案

JLS 提到在构造后修改最终字段是有问题的 - 请参阅 17.5. final Field Semantics

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. Subsequent Modification of final Fields:

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.

除此之外,JavaDocs of Field.set还包括关于此的警告:

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 利用语言规范授予的重新排序和缓存可能性。

关于java - 用反射覆盖最终静态字段是否有限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53395929/

相关文章:

java - 如何使用 Spring Boot 将生成的 PDF 文件保存到 MySQL 数据库?

java - Tomcat 7.0.42 -> 7.0.47 在 EL-Escaping 中发生了什么变化?

java - Hibernate Maven 增强插件配置

java - 在java中旋转3d vector

java - Struts2 JSON转Java Action Object(找不到错误)

c# - 任何方法类型的委托(delegate) - C#

java - HADOOP - 如何在 map 中动态加载类

javascript - 如何返回分配给未命名函数的属性的名称

.net - 调用和 Callvirt

java - 在 Java Android 中动态编辑/创建类