歡迎加入Java交流群 512878347 ,歡迎關注微信公眾號 以文在線 。
局部內部類是在方法中定義的類。它的可見范圍是當前方法,和局部變量一樣,局部內部類不能用訪問控制修飾符(public、private以及protected)和靜態修飾符static來修飾。局部內部類中除了可以訪問外部類的所有成員,還可以訪問所在方法的最終變量或參數(被final修飾的變量或參數),從JDK8.0開始,還可以訪問所在方法的實際上的最終變量或參數(沒有被final修飾但只進行了一次賦值的變量或參數)。為什么局部內部類中只能訪問所在方法的最終變量或參數以及實際上的最終變量或參數呢?
先來驗證局部內部類中只能訪問所在方法的最終變量或參數以及實際上的最終變量或參數。如程序1所示,在外部類Out_1的method方法中有一個參數a,兩個變量b、c,並在局部內部類Inner_1中訪問了這三個量。由於參數a是被final修飾的,那參數a就是最終的參數,故在Inner_1中可以訪問它,第7句代碼沒有編譯錯誤;雖然變量b沒有被final修飾,但是變量b僅僅被賦值一次,它是實際上的最終變量,故在Inner_1中可以訪問它,第8句代碼沒有編譯錯誤;變量c沒有被final修飾並且被賦值了兩次,它不是最終變量也不是實際上的最終變量,故在Inner_1中不可以訪問它,第9句代碼就有了編譯錯誤。可以得到,在局部內部類中只能訪問所在方法的最終變量或參數以及實際上的最終變量或參數。
1 class Out_1 { 2 public void method(final int a) { 3 final int b = 1; 4 int c = 2; 5 c = 3; 6 class Inner_1 { 7 int d = a; //編譯正確。
8 int e = b; //編譯正確。
9 int f = c; //編譯錯誤。
10 } 11 } 12 }
程序1 Out_1.java
接着再來討論為什么局部內部類中只能訪問所在方法的最終變量或實際上的最終變量。這里只討論方法的變量,對於方法的參數它的道理是一模一樣的,因為方法的參數本質上就是方法的變量。要知道局部內部類和外部類是處於同一級別的,局部內部類不會因為定義在方法中就隨着方法的執行完畢而銷毀。如程序2所示,在外部類Out_2的method1方法中定義了一個局部內部類Inner_2,然后創建了一個線程t並啟動它,然后method1方法就執行結束,局部變量m就被銷毀。線程t啟動之后,會先睡眠1000毫秒,然后創建了局部內部類Inner_2的對象,並通過該對象去調用了method2方法,在method2方法中訪問了method1方法定義的變量m,由於method1方法早已執行結束,變量m已經消失。這樣就出現了一個矛盾:內部類對象訪問了一個不存在的變量。
1 class Out_2 { 2 public void method1() { 3 int m = 4; 4 class Inner_2 { 5 public void method2() { 6 System.out.println(m); 7 } 8 } 9 new Thread("t") { 10 public void run() { 11 try { 12 Thread.sleep(1000); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 new Inner_2().method2(); 17 } 18 }.start(); 19 } 20 }
程序2 Out_2.java
為了解決這個矛盾,如果局部內部類中訪問了所在方法的某個變量,就將該方法中的變量復制一份作為內部類的成員變量,當內部類訪問所在方法中的變量時,就讓它去訪問復制出來的成員變量。這樣在內部類所在方法中的變量消失的之后,仍然可以訪問它,當然這里並不是真正地訪問它,而是訪問它的復制品。這里需要注意,由於是將局部內部類所在方法的變量復制一份作為局部內部類的成員變量,故在定義局部內部類之前,一定要對局部內部類所在方法的變量進行初始化,沒有初始化是無法復制的。在程序3所示的代碼中,第7句代碼是有編譯錯誤的。
1 class Out_3 { 2 public void method1() { 3 int m; 4 class Inner_3 { 5 public void method2() { 6 // 編譯錯誤,m應在定義內部類之前初始化。
7 m = 4; 8 } 9 } 10 } 11 }
程序3 Out_3.java
但是這時又有一個問題,那就是必須時時刻刻保證復制得到的那一份成員變量的值和原來的局部變量的值相同。如果在外部類中修改了局部變量的值,那就要修改局部內部類中復制得到的那一份成員變量的值;如果在局部內部類中修改了復制得到的那一份成員變量的值,那就要修改外部類中局部變量的值(前提是這個局部變量還存在),這樣做是非常困難的。於是Java干脆就不允許局部內部類要訪問的局部變量的值發生改變,即局部內部類中只能訪問所在方法的最終變量或實際上的最終變量。