這是java的一條規則。那么為什么會有這條規則呢?要想弄懂這個問題,就需要弄懂局部內部類對象和局部變量的生命周期誰更長的問題。
首先,看一段代碼,以沒有將變量聲明為final的代碼作為例子,代碼如下:
1 class Outer{ 2 3 Object obj; 4 public void outerMethod() { 5 6 //局部變量 7 int x = 5; 8 //定義在方法中的內部類稱為局部內部類 9 class Inner{ 10 11 public String toString() { 12 System.out.println(x);//訪問了局部變量x
return null; 13 } 14 } 15 //創建內部類實例 16 Inner in = new Inner(); 17 in.toString(); 18 //將內部類實例的引用賦值給obj 19 obj = in; 20 } 21 } 22 public class HelloDemo { 23 24 public static void main(String[] argr) { 25 Outer out = new Outer(); 26 out.outerMethod(); 27
28 } 29 }
如上面的第7行代碼所示,變量x沒有被聲明為final,如果是這樣的話,當執行完第26行的outMethod()方法后,outMethod()方法將出棧,出棧后outMethod()方法里面定義的所有變量(x 和 in)都死亡了,(但是此時內部類的對象還活着,直到它不再被使用才會被回收,也就是說此內部類對象的生命周期比局部變量的生命周期長),並且在變量 in 死亡之前,in 的值賦值給了成員變量obj(第19行代碼),這時obj 指向了內部類的對象,如果此時在27行執行一條代碼: out.obj.toString();那么這條代碼將會訪問到局部變量 x,但是此時 x 已經死亡了,內部類對象已經訪問不到 x 了,因此這是相互矛盾的。所以上面的代碼實際上並不能編譯通過。(在jdk8.0能編譯通過,那是因為它檢測到局部內部類訪問了 x,會默認給 x 的前面加上隱式的final,如果在第8行加上一句代碼:x = 4;編譯器將會報錯,因為final不允許 x 的值改變)
如果局部變量 x 被聲明為final后(在第七行的int前加上final),x 就代表了一個常量,那么第15行的代碼實際上就變成了 System.out.println(5);,這時內部類相當於訪問了一個數字5。這是沒任何問題的,因此局部內部類訪問它所在方法的局部變量時,要求該局部變量必須聲明為final。究其根本原因,是局部內部類對象的生命周期比局部變量的生命周期長。
2018-05-11 22:10:16