无法在没有Exception的情况下修改私有最终静态变量

时间:2011-08-30 09:40:00

标签: java reflection

我尝试修改私有的最终静态变量,如this

    ...try {

        Field f =TargetA.class.getDeclaredField("RECV_TIMEOUT");
        f.setAccessible(true);

        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);

        f.set(null, 12L);

    } catch (Exception e) {
        e.printStackTrace();//not reach here!
    } ...

    class TargetA{
        private static final long RECV_TIMEOUT = 180000L;
     }

但是TargetA.RECV_TIMEOUT仍然是180000L,没有任何异常。 我在StackOverflow中搜索了这个问题,但找不到解决方案。

我猜Java版本1.6在反射中有更多限制,这违反了OO规则。 谢谢你的建议!

3 个答案:

答案 0 :(得分:7)

您可以通过这种方式更改静态最终字段,如果使用反射查看值,则会更改。您遇到的问题是编译器在编译时只进行一次内联常量优化。这意味着值可以更改,但是使用常量的位置不会更改。

解决这个问题的方法是使用包装器方法来“混淆”编译器,这样就不必改变使用常量的方式。

public static final long RECV_TIMEOUT = runtime(180000L);

public static final <T> T runtime(T t) { return t; }

答案 1 :(得分:5)

通过反射修改final字段有许多限制。特别是,如果final字段由编译时常量初始化,则可能无法观察到其新值(JLS 17.5.3 Subsequent Modification of Final Fields)。

您可以使用以下解决方法:

class TargetA{
     private static final long RECV_TIMEOUT = defaultTimeout();     

     private static long defaultTimeout() { return 180000L; }
} 

答案 2 :(得分:0)

尝试在更改后添加以下代码,您会看到它已更改。

Field e =TargetA.class.getDeclaredField("RECV_TIMEOUT");
e.setAccessible(true);
System.out.println(e.getLong(modifiersField));

然而引自彼得

  

这意味着价值可以改变,但是那里的地方   使用的常数不会改变。