1. 程式人生 > >【小家java】final修飾的變數真的不可變嗎?

【小家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關鍵字的瞭解

—-題後語—-

我的微信公眾號也會持續推送技術乾貨,歡迎下方二維碼掃碼關注獲取。
這裡寫圖片描述
更多內容持續更新中,歡迎關注我的部落格!
有任何問題,可以跟我留言討論,歡迎指正,不勝感激。
有任何疑問,亦可掃碼向我提我喲~