【小家java】final修飾的變數真的不可變嗎?
相關閱讀
1、概述
這可能是大家的一個共識:如果我們希望這個變數不可變,我們可以用final進行修飾。但本篇將帶你深入瞭解不變的含義,我相信可以讓你更深的瞭解final的原理,也能記得更牢靠
2、栗子
被final修飾過的變數,只是說棧儲存的地址不能再改變,但是卻沒有說地址指向的內容不能改變。所以用final修飾,但內容是個物件啥的,然後改變物件屬性值,這個不在本文討論的範圍以內。本文想討論的是,直接就概念final的棧的地址,讓它去指向另外一塊記憶體地址。比如下面直接暴力反射處理,顯然是不好使的:
private static final String str = "abc";
private final String str2 = "efg";
@Test
public void fun1() throws Exception {
System.out.println(str); //abc
System.out.println(str2); //efg
///////////////////////
Field field = Tests.class.getDeclaredField("str");
field.setAccessible(true);
field.set(this, "cba"); //java.lang.IllegalAccessException: Can not set static final
System.out.println(str);
}
接下來,就好好看看我是怎麼改變這個值的:
private static final String str = "abc";
private final String str2 = "efg";
@Test
public void fun1() throws Exception {
System.out.println(str); //abc
System.out.println(str2); //efg
///////////////////////
Field field = Tests.class.getDeclaredField ("str2");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, "gfe");
System.out.println(str2); //efg
}
詫異吧,我們會發現最後輸出的還是efg,啪啪打臉啊,但是我debug一下,看到是如下情況:
這裡面我解釋兩個東西:
1、為什麼能夠撼動final的值?
field.getModifiers()&~Modifier.FINAL 這句話就是去掉final。其實java的訪問許可權資訊啥的都是以2的N次冪來作為表示的,具體都是在java.lang.reflect.Modifier這個類裡。so,咱們都把它的修飾符幹掉,當然可以對Field set值了
所以,java的反射機制直接打破了封裝有木有,哈哈哈
2、為什麼最終列印的和我們除錯的值不一樣?
System.out.println(str2); //efg
System.out.println(field.get(this)); //gfe 通過反射拿到的值是對的
我們通過反射拿到的值是正確的,而直接輸出變數的值卻是不對的。究其原因:這其實是Java編譯器對 final 屬型的內聯優化(java的內聯機制和jvm底層有關,對程式調優有非常重要的作用。後續JVM相關博文,我會重點討論),即編譯時把該 final 的值直接放到了引用它的地方。即使是反射修改了該屬性,但這種事後處理於事無補。
所以,咱們確實是可以通過反射來修改final的值,但是我們在後續程式碼中卻不能用,尷尬。為了解決這個問題,設計的面實在是有點多,所以此處不適合展開來說。等後面講述了JVM相關的東西后,會回到這裡補充,請持續關注。。。
但是,請大家可以記住一個結論:
只要不會被編譯器內聯優化的 final 屬性就可以通過反射有效的進行修改 – 修改後程式碼中可使用到新的值
3、使用場景
幾乎沒啥使用場景,除非一些極限情況:比如強制修改第三方原始碼。。。
4、最後
整理出來的內容,希望能加深大家對final關鍵字的瞭解
—-題後語—-
我的微信公眾號也會持續推送技術乾貨,歡迎下方二維碼掃碼關注獲取。
更多內容持續更新中,歡迎關注我的部落格!
有任何問題,可以跟我留言討論,歡迎指正,不勝感激。
有任何疑問,亦可掃碼向我提我喲~