2012-12-31 84 views
5

我最近偶然發現了Change private static final field using Java reflection和測試的polygenelubricants'EverythingIsTrue類,工作正常,System.out.format("Everything is %s", false);打印Everything is true確實。但是,當我改變代碼爲無法使用java反射更改靜態最終字段?

public class EverythingIsTrue { 

    public static final boolean FALSE = false; 

    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 { 
     setFinalStatic(EverythingIsTrue.class.getField("FALSE"), true); 
     System.out.format("Everything is %s", FALSE); 
    } 
} 

它打印

Everything is false 

有誰知道爲什麼嗎? setFinalStatic實際上是否工作?

回答

13

當訪問原始靜態final字段時,Java編譯器將假定該值是一個常量並且嵌入該值,而不是生成訪問該字段的代碼。這意味着編譯器將替換爲FALSE字段的參考,其值爲false。如果使用反射來訪問該字段,則會看到該字段的值實際上已更改。

這不適用於非原始字段,因爲對象引用的值在編譯時不能內聯。

+1

這是完全一樣的答案,因爲我的 - 只能用更多的話! – dty

+0

@dty它看起來像是無限的更多的單詞;) –

+0

我刪除了我的答案,因爲你的答案已經被接受了,而我的答案並沒有增加任何價值 - 只是簡潔! ;-) – dty

17

通過調用方法調用的結果可以避免編譯器內聯,即使是虛擬調用也是如此。

public class Main { 
    // value is not known at compile time, so not inlined 
    public static final boolean FLAG = Boolean.parseBoolean("false"); 

    static void setFinalStatic(Class clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException { 
     Field field = clazz.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     Field modifiers = field.getClass().getDeclaredField("modifiers"); 
     modifiers.setAccessible(true); 
     modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 

    public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", FLAG); 
     setFinalStatic(Main.class, "FLAG", true); 
     System.out.printf("Everything is %s%n", FLAG); 
    } 
} 

打印

Everything is false 
Everything is true 
+0

這太棒了!使用通用方法,您可以爲非基元構造虛擬方法'static T placeHolder(){return null; }' – flakes

+0

@ mc-emperor,以前的版本更具可讀性。 – dit

+0

@dit它是控制檯輸出,所以它不應該被格式化,就好像它是源代碼。 –

相關問題