大部分時候,類被定義成一個獨立的程序單元。在某些情況下,也會把一個類放在另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類,包含內部類的類也被稱為外部類。
class Outer { private int a;
public class Inner { private int a; public void method(int a) { a++; // 局部變量 this.a++; // Inner類成員變量 Outer.this.a++; // Outer類成員變量 } } }
對於上面的成員內部類,一般做法是在Outer中寫一個返回Inner類對象的方法
public Inner getInner() { return new Inner(); }
在其他類中使用該成員內部類:
Outer outer = new Outer(); Outer.Inner inner = outer.getInner(); // 或者Outer.Inner inner = outer.new Inner();
而對於靜態內部類,就不需要創建外部類的實例了:
Outer.StaticInner inner = new Outer.StaticInner();
匿名內部類不能訪問外部類方法中的局部變量,除非該變量被聲明為final類型
1. 這里所說的“匿名內部類”主要是指在其外部類的成員方法內定義的同時完成實例化的類,若其訪問該成員方法中的局部變量,局部變量必須要被final修飾。原因是編譯器實現上的困難:內部類對象的生命周期很有可能會超過局部變量的生命周期。
2. 局部變量的生命周期:當該方法被調用時,該方法中的局部變量在棧中被創建,當方法調用結束時,退棧,這些局部變量全部死亡。而內部類對象生命周期與其它類對象一樣:自創建一個匿名內部類對象,系統為該對象分配內存,直到沒有引用變量指向分配給該對象的內存,它才有可能會死亡(被JVM垃圾回收)。所以完全可能出現的一種情況是:成員方法已調用結束,局部變量已死亡,但匿名內部類的對象仍然活着。
3. 如果匿名內部類的對象訪問了同一個方法中的局部變量,就要求只要匿名內部類對象還活着,那么棧中的那些它要所訪問的局部變量就不能“死亡”。
4. 解決方法:匿名內部類對象可以訪問同一個方法中被定義為final類型的局部變量。定義為final后,編譯器會把匿名內部類對象要訪問的所有final類型局部變量,都拷貝一份作為該對象的成員變量。這樣,即使棧中局部變量已經死亡,匿名內部類對象照樣可以拿到該局部變量的值,因為它自己拷貝了一份,且與原局部變量的值始終保持一致(final類型不可變)。
最后,Java 8更加智能:如果局部變量被匿名內部類訪問,那么該局部變量相當於自動使用了final修飾。