我注意到一件非常奇怪的事情,在通过反射更改最终字段后,返回该字段的方法始终给出旧值。我想这可能是因为 JIT 编译器。
这是示例程序:
public class Main
{
private static final Main m = new Main();
public static Main getM()
{
return m;
}
public static void main(String args[]) throws Exception
{
Main m = getM();
int x = 0;
for(int i = 0;i<10000000;i++)
{
if(getM().equals(m))
x ++;
}
Field f = Main.class.getDeclaredField("m");
f.setAccessible(true);
removeFinal(f);
Main main1 = new Main();
f.set(null, main1);
Main main2 = (Main) f.get(null);
Main main3 = getM();
System.out.println(main1.toString());
System.out.println(main2.toString());
System.out.println(main3.toString());
}
private static void removeFinal(Field field) throws NoSuchFieldException, IllegalAccessException
{
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
结果是:
Main@1be6f5c3
Main@1be6f5c3
Main@6b884d57
我想知道,如何使 getM() 返回更新后的值?
我想知道,如何使 getM() 返回更新后的值?
到了决赛,你就不能了。返回“旧”值是合法行为,因为JLS 17.5.3 https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5.3:
即便如此,仍然存在许多并发症。如果最后一个字段是
初始化为字段中的常量表达式(第 15.28 节)
声明,对最终字段的更改可能不会被观察到,因为
该最终字段的使用在编译时被替换为值
的常量表达式。
另一个问题是规范允许激进的
最终字段的优化。在一个线程内,允许
使用最终字段的修改重新排序最终字段的读取
不发生在构造函数中的字段。
请参阅该章中包含的指导性示例。
克服这一规定的尝试必须包括扰乱堆栈中的优化器,而且充其量也是脆弱的。如果您选择修改字段,那么根据定义,这些字段不应是最终字段。如果您出于性能原因想要这样做(真的吗?),那么 JSR 292 提供了执行此操作的机制“几乎最终” https://code.google.com/p/jsr292-cookbook/source/browse/trunk/almost-final/src/jsr292/cookbook/almostfinal/建筑。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)