以[final int x=911] , [static final int x=912]為例,jdk1.6.0_16(為何如此版本詳細,是因為下面還有個jdk的bug).
樣例類:
class Test {
private final int x=911;//modifiers:final->18,non-final->2
static final private int y=912;//modifiers:final->26,non-final->10
public int getX(){
return x;
}
public static int getY(){
return y;
}
}
Java中的final field意指常量,賦值一次,不可改變.編譯器會對final field進行如下的優化:
e.g:
Test t=new Test();
凡是在程序中對t.x的引用,編譯器都將以字面值911替換,getX()中的return x也會被替換成return 911;
所以就算在運行時你改變了x的值也無濟于事,編譯器對它們進行的是靜態編譯.
但是Test.class.getDeclaredField("x").getInt(t)除外;
那么如何在運行時改變final field x的值呢?
private final int x=911;Field.modifiers為18,而private int x=911;Field.modifiers為2.
所以如果我們修改Field[Test.class.getDeclaredField("x")].modifiers由18[final]變為2[non-final],那么你就可以修改x的值了.
Test tObj=new Test();
Field f_x=Test.class.getDeclaredField("x");
//修改modifiers 18->2
Field f_f_x=f_x.getClass().getDeclaredField("modifiers");
f_f_x.setAccessible(true);
f_f_x.setInt(f_x, 2/*non-final*/);
f_x.setAccessible(true);
f_x.setInt(tObj, 110);//改變x的值為110.
System.out.println("靜態編譯的x值:"+tObj.getX()+".------.運行時改變了的值110:"+f_x.getInt(tObj));
f_x.setInt(tObj, 111);//你可以繼續改變x的值為.
System.out.println(f_x.getInt(tObj));
但是想恢復原來的modifiers,f_f_x.setInt(f_x, 18/*final*/);這是無效的,因為Field只會初始化它的FieldAccessor引用一次.
在上面的過程中,我還發現了個jdk bug,你如果將上面的紅色代碼改為如下的代碼:
f_f_x.setInt(f_x, 10/*這個數值是static non-final modifiers,而x是non-static的,這樣就會使f_x得到一個static FieldAccessor*/);那么會引發A fatal error has been detected by the Java Runtime Environment.并產生相應的err log文件.顯然JVM沒有對這種情況加以處理.我已提交to sun bug report site.
sun 于2010-3-26通知我,他們已承認該bug,bug id : 6938467.發布到外網可能有一到兩天的延遲.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6938467