我注意到一个非常奇怪的事情,通过Reflection更改最终字段后,返回该字段的方法始终给出旧值。我想这可能是因为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()返回更新值?
答案 0 :(得分:12)
我想知道,我如何让getM()返回更新值?
决赛时,你不能。返回“旧”值是合法行为,如JLS 17.5.3:
即使这样,也有许多并发症。如果是最终字段 在字段中初始化为常量表达式(第15.28节) 声明,可能无法观察到对最终字段的更改,因为 在编译时将该最终字段的使用替换为该值 常数表达式。
另一个问题是规范允许攻击性 最终字段的优化。在一个帖子中,允许 使用final的修改重新排序最终字段的读取 不在构造函数中发生的字段。
请参阅该章中包含的指导性示例。
尝试克服这一规定必须包括在堆栈中弄乱优化器,并且充其量是脆弱的。如果您选择修改字段,那么根据定义,这些字段不应该是最终字段。如果你出于性能原因想要这个(你真的吗?),那么JSR 292提供了执行"almost final"构造的机制。
答案 1 :(得分:1)
如果您真的想要更改该值,可以包装该类并覆盖getter。
也许您已经听说过“委托”/“代理”模式。