Java進階——Java 區域性內部類訪問區域性變數為什麼必須加final關鍵字
Java 區域性內部類訪問區域性變數為什麼必須加final關鍵字
疑問
在Java中,區域性內部類如果呼叫了方法中的變數,那麼該變數必須申明為final型別,如果不申明,則編譯就會出錯。
這裡的內部類指的是方法內部類或匿名內部類,不包含靜態內部類和成員內部類
這裡通過一個例子類分析
public class InnerClass { private int defaultAge = 5; 區域性變數 age,必須新增final關鍵字,這裡先不加 public void addAge( int age){ //區域性內部類 class NewAge{ private int getAge(){ return age + defaultAge; } } NewAge newAge = new NewAge(); System.out.print(newAge.getAge()); } }
強行不加final,編譯,則會報錯:
Error:(16, 12) 錯誤: 從內部類中訪問本地變數age; 需要被宣告為最終型別
分析
-
原因
1.生命週期不同: 為什麼必須區域性變數加final關鍵字呢?因為區域性變數直接儲存在棧中,當方法執行結束,非final的區域性變數就被銷燬,而區域性內部類對區域性變數的引用依然存在,當局部內部類要呼叫區域性變數時,就會出錯,出現非法引用。簡單來說,就是非final的區域性變數的生命週期比區域性內部類的生命週期短
,是不是直接可以拷貝變數到區域性內部類?這樣內部類中就可以使用而且不擔心生命週期問題呢?也是不可以的,因為直接拷貝又會出現第二個問題,就是資料不同步
2.資料不同步
所以使用final避免資料不同步的問題
-
原理
那為什麼新增final修飾的區域性變數,就可以被區域性內部類引用呢?
若定義為final,則java編譯器則會在內部類NewAge內生成一個外部變數的拷貝,而且可以既可以保證內部類可以引用外部屬性,又能保證值的唯一性
也就是拷貝了一個變數的副本,提供給區域性內部類,這個副本的生命週期和區域性內部類一樣長,並且這個副本不可以修改,保證了資料的同步
注意:
在Java8 中,被區域性內部類引用的區域性變數,預設新增final,所以不需要新增final關鍵詞
-
位元組碼
如果有興趣,可以看看編譯後的位元組碼,即.class檔案
class InnerClass$1NewAge {
//可以看到,區域性內部類中的使用的age,是通過建構函式傳遞進來,並不是直接引用外部變數。
InnerClass$1NewAge(InnerClass var1, int var2) {
this.this$0 = var1;
this.val$age = var2;
}
private int getAge() {
return this.val$age + InnerClass.access$000(this.this$0);
}
}
InnerClass類編譯後,在資料夾會出現InnerClass.class和InnerClass$1NewAge.class,這說明外部類的方法 和內部類處於同一級。
結論
區域性內部類引用區域性變數,不新增final,會出現生命週期不同,導致非法引用問題,而且直接拷貝會出現資料不同步問題,所以使用final,保證了合法引用,而且資料不可修改