2011-08-30 74 views
2

我試圖修改私人最終靜態變量像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規則。 感謝您的諮詢!

回答

7

你可以改變靜態最終場這樣的,如果你使用反射看看它的值將被改變。你所遇到的問題是,編譯器只進行一次並且只有一次優化,即在編譯時內聯常量。這意味着該值可以改變,但常數使用的地方不會改變。

解決此問題的方法是使用包裝器方法來「混淆」編譯器,從而避免必須更改常量的使用方式。

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

public static final <T> T runtime(T t) { return t; } 
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; } 
} 
0

嘗試在更改後添加以下代碼,您將看到它已更改。

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

但是由Peter

援引這意味着該值可以變化,但是在 常數時不發生變化的地方。