局部內部類是定義在方法體或代碼塊中的類,在筆記19中已有過簡單介紹。 今天要討論的是局部內部類為什麼只能訪問為常量的局部變數。 作者: 博客園--蟬蟬 請尊重作者勞動成果,轉載請在標題註明“轉載”字樣,並標明原文鏈接: http://www.cnblogs.com/chanchan/p/840241 ...
局部內部類是定義在方法體或代碼塊中的類,在筆記19中已有過簡單介紹。
今天要討論的是局部內部類為什麼只能訪問為常量的局部變數。
作者: 博客園--蟬蟬
請尊重作者勞動成果,轉載請在標題註明“轉載”字樣,並標明原文鏈接:
http://www.cnblogs.com/chanchan/p/8402411.html
參考資料:
http://www.cnblogs.com/dolphin0520/p/3811445.html
1.首先來瞭解一下局部內部類是如何訪問局部變數的
Person類是外部類,LoInClassIntf是介面,localInClassRe是Person類的成員方法,且返回值類型為LoInClassIntf;
方法內定義了一個局部內部類LoInnerClass,該內部類實現了介面LoInClassIntf;
方法內還定義了一個final的局部變數a,定義了一個LoInnerClass類型的對象引用loInC;
代碼如下:
1 //筆記23:內部類--局部內部類--實現介面,返回內部類對象 2 //介面 3 public interface LoInClassIntf { 4 void test(); 5 } 6 7 //方法localInClassRe,返回值為LoInClassIntf,局部內部類來實現該介面,向上轉型 8 public LoInClassIntf localInClassRe() { 9 final int a = 1; //常量 10 11 //筆記23--內部類--局部內部類--實現介面 12 class LoInnerClass implements LoInClassIntf { 13 public void test() { 14 System.out.println("variable a:" + a); 15 } 16 } 17 18 LoInnerClass loInC = new LoInnerClass(); 19 return loInC; 20 } 21 22 public static void main(String[] args) { 23 //筆記23--局部內部類 24 Person per = new Person(); 25 26 LoInClassIntf lInCIntf = per.localInClassRe(); 27 lInCIntf.test(); 28 }
輸出結果為:
1 variable a:1
成員方法localInClassRe執行中的記憶體示意圖如下:
成員方法localInClassRe執行後的記憶體示意圖如下:
分析:
成員方法localInClassRe執行後,方法體中的局部變數a和對象引用loInC都被釋放掉了,但分配在堆中的對象未回收,這時由main方法中的局部變數lInCIntf來指向它;
到這裡還沒什麼問題,但第27行,lInCIntf調用了test方法,test方法中訪問到了成員方法localInClassRe中的局部變數a,而a此時已不存在了,所以就會出現錯誤;
即,局部變數與局部內部類的對象的生命周期不同;
為解決這一問題,Java把局部內部類要訪問的局部變數重新拷貝了一份,並把備份放在內部類的常量池中,這樣不論方法有沒有執行結束,拷貝都是存在的,就不會再出現訪問不存在的變數的錯誤了。
成員方法localInClassRe執行中的記憶體示意圖如下:
成員方法localInClassRe執行後的記憶體示意圖如下:
上面涉及到的局部變數a是方法體內定義的,如果局部內部類訪問的是方法體的參數呢?
Java採取的方法是,預設為局部內部類的構造方法傳入該參數作為構造方法的參數,然後用該參數來初始化內部類中拷貝的變數a。
局部內部類如何訪問局部變數的問題解決了,那麼為什麼只能訪問final的局部變數呢?
2.數據同步的問題
上面通過拷貝一份局部變數來解決生命周期不同的問題,如果方法體和局部內部類都改變了a的值會怎麼樣呢?
如下圖所示:
這樣兩個a不一致,就會出現數據不同步,下一步應該用a=2還是a=3呢?
為解決這個問題,Java規定局部內部類可訪問的局部變數必須為final的,即內部類不能改變要訪問的局部變數的值,這樣就不會出現數據不同步的問題了。