2012-02-22 72 views
4

我試圖改變一個私有靜態final字段與Java反射。 但它只是失敗。有人有想法嗎?Java反射,改變私有靜態final字段沒有做任何事

這裏是我的代碼:

public class SecuredClass { 
    private static final String securedField = "SecretData"; 
    public static String getSecretData() { return securedField; } 
} 

public class ChangeField { 
    static void setFinalStatic(Field field, Object newValue) throws Exception 
    { 
     field.setAccessible(true); 

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

     field.set(null, newValue); 
    } 

    public static void main(String args[]) throws Exception 
    { 
     System.out.println("Before = " + SecuredClass.getSecretData()); 

     Field stringField = SecuredClass.class.getDeclaredField("securedField"); 
     stringField.setAccessible(true); 
     setFinalStatic(stringField, "Screwed Data!"); 


     System.out.println("After = " + Java_FileMerger_Main.getSecretData()); 
    } 
} 

這是我的輸出:

Before = SecretData 
After = SecretData 

我試圖刪除與System.setSecurityManager的安全管理器(空); 但它沒有改變任何東西。 我知道這是邪惡的,但我想讓它工作。 我希望有人能幫助我。

+0

你「後」的價值來自於'Java_FileMerger_Main'不'SecuredClass' – DNA 2012-02-22 08:35:09

+0

的Java反射不是用來改變類的字節碼的。這個限制也是出於安全原因。例如'setInt(42)'將與'SecuredClass.securedField = 42;'相同。 – 2012-02-22 08:38:48

回答

6

未定義通過反射改變最終字段的語義(參見specification)。不能保證在從田間讀書時,永遠不會看到對最終場的反思寫作。

尤其是對於具有常量賦值的靜態final字段(在您的情況下),編譯器通常會內聯該常量,因此該字段在運行時不會被訪問(僅當您使用反射時)。

SecurityManager在此處不相關。如果使用反射會被SecurityManager禁止,該操作會拋出異常,並且不會自動失敗。

Change private static final field using Java reflection這個問題的答案提供了一些更多的細節,包括一個例子,你可以在字節代碼中看到常量被內聯。

+0

非常感謝您的回答! – Nicnl 2012-02-22 09:03:57

1

我也有上面給出的例子的問題。 對我來說,它沒有將字符串定義爲字面而是作爲對象。

private static final String SECURED_FIELD = String.valueOf("SecretData"); 

或者

private static final String SECURED_FIELD = new String("SecretData"); 

如果你不希望從文字中獲益,但我沒有去關心它的最佳解決方案。

1

有一個解決這個問題的方法。如果設置在靜態提交的私有靜態最後的值{}塊,將工作,因爲它不會內聯領域:

private static final String MY_FIELD; 

static { 
    MY_FIELD = "SomeText" 
} 

... 

Field field = VisitorId.class.getDeclaredField("MY_FIELD"); 

field.setAccessible(true); 
field.set(field, "fakeText");