為什麼需要內部類?
- 訪問外部私有成員:內部類可無限制訪問外部類所有成員(含 private)。
- 封裝隱藏:對同包其他類不可見,實現更細粒度的封裝。
- 回調便利:匿名內部類實現單方法介面極其簡潔(Lambda 前身)。
四大類型對比
| 類型 | 定義位置 | 訪問外部實例 | 典型用途 | 語法關鍵字 |
| 成員內部類 | 類內部、方法外 | 隱式持有 Outer.this | 邏輯強綁定、多實例共享外部狀態 | 無 |
| 靜態嵌套類 | 類內部、方法外 | 無(無 Outer.this) | 工具類、數據容器、獨立邏輯 | static |
| 局部內部類 | 方法/代碼塊內 | 持有外部實例 + 複製 final 變量 | 僅在單方法內使用、實現介面 | 無 |
| 匿名內部類 | 表達式中 | 同局部類 | 一次性實現介面/繼承類、事件監聽 | new Interface() { ... } |
成員內部類深度解析
public class Outer {
private int value = 10;
public class Inner {
public void print() { System.out.println(value); // 直接訪問 private }
}
public void demo() { new Inner().print(); }
}
// 編譯器生成:
// class Outer$Inner { final Outer this$0; Outer$Inner(Outer o){ this$0=o; } void print(){ System.out.println(this$0.value); } }
- 外部實例引用名為
this$0(合成字段),構造器由編譯器注入。
- 外部創建:
Outer.Inner in = outer.new Inner(); 或外部類方法內 new Inner() 隱含 this。
- 訪問外部
this:Outer.this.value。
局部內部類與 final 變量捕獲
public void start(final boolean beep) {
class TimerTask implements Runnable {
public void run() { if (beep) Toolkit.getDefaultToolkit().beep(); }
}
new Thread(new TimerTask()).start();
}
// 編譯器生成:class Outer$1TimerTask { final boolean val$beep; ... }
// beep 被複製到構造器參數並存入 val$beep,保證生命週期超過方法棧幀
- JDK 8+ effectively final:變量未重新賦值即可隱式 final,無需寫
final。
- 需修改捕獲變量?用單元素數組
final int[] counter = {0}; 修改 counter[0]++。
匿名內部類:Lambda 前身
button.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) { System.out.println("click"); }
});
// 語法:new SuperType(args) { body }
// SuperType 為介面 → 實現;為類 → 繼承。
// 無構造器,若需參數用實例初始化塊 { }。
靜態嵌套類:獨立的內部類
public class ArrayAlg {
public static class Pair { // 靜態嵌套類
private double first, second;
public Pair(double f, double s) { first=f; second=s; }
public double getFirst() { return first; }
}
public static Pair minmax(double[] a) { ... return new Pair(min, max); }
}
// 用法:ArrayAlg.Pair p = ArrayAlg.minmax(data);
- 無
Outer.this,可獨立實例化:new Outer.StaticNested()。
- 常用於「邏輯歸屬外部類但無需外部實例」的場景(如 Builder、Node、Entry)。
編譯器視角:內部類是語法糖
- JVM 只知道普通類,
Outer$Inner.class 即內部類字節碼文件。
- 私有訪問通過合成
access$0(Outer) 靜態方法實現(字節碼層面 package-private)。
- 反編譯工具(javap -private)可見合成字段/構造器/訪問器方法。