內部類是 Java 進階編程的核心特性,類似 C++ 巢狀類但更強大 (擁有外部類引用)。掌握內部類能設計出更優雅、封裝性更強的程式結構。
| 類型 | 定義位置 | 外部類引用 | 存取修飾詞 | 典型用途 |
|---|---|---|---|---|
| 成員內部類 (非靜態) | 類內部、方法外 | 有 | public/protected/private | 實作介面、封裝實作細節 |
| 靜態內部類 | 類內部、方法外 | 無 | public/protected/private | 工具類、快取、Builder 模式 |
| 局部內部類 | 方法/程式碼區塊內 | 有 | 無 (隱含包級可見) | 單次使用、邏輯封裝 |
| 匿名內部類 | 表達式中 | 有 | 無 | 事件監聽、回調、一次性介面實作 |
public class Goods {
private int valueRate = 2;
private class Content implements Contents {
private int i = 11 * valueRate; // 直接存取外部 private 欄位
public int value() { return i; }
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) { label = whereTo; }
public String readLabel() { return label; }
}
public Destination dest(String s) { return new GDestination(s); }
public Contents cont() { return new Content(); }
}
Goods.this,內部實例持有外部實例引用private)Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();OuterClass.this (解決同名欄位遮蔽)static 修飾,不持有外部實例引用,類似 C++ 巢狀類Outer.StaticInner si = new Outer.StaticInner(); (無需外部實例)Builder 模式、快取實作、封裝輔助類public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination { // 方法內定義
private String label;
private GDestination(String whereTo) { label = whereTo; }
public String readLabel() { return label; }
}
return new GDestination(s);
}
}
if 區塊).class 檔 (如 Goods1$1GDestination.class)private void internalTracking(boolean b) {
if (b) {
class TrackingSlip { // 僅在 if 區塊可見
private String id;
TrackingSlip(String s) { id = s; }
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
// new TrackingSlip() 編譯錯誤:超出作用域
}
public class Goods3 {
public Contents cont() {
return new Contents() { // 實作介面
private int i = 11;
public int value() { return i; }
}; // 注意分號結尾
}
}
// 事件監聽經典用法
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
| 限制 | 解決方案 |
|---|---|
| 無建構子 (無名稱) | 1. 利用外部方法參數 (需 final) 2. 改為具名局部內部類 3. 使用實例初始化區塊 { ... } |
| 無法實作多介面 | 改用具名內部類 |
| 序列化風險 (合成名稱不穩定) | 避免序列化匿名類實例 |
Java 單繼承限制,但內部類 + 介面可優雅實現多重繼承效果:
public class A {
// 需同時實作 Interface1 與 Interface2,但方法簽名衝突
class Inner1 implements Interface1 { ... }
class Inner2 implements Interface2 { ... }
}