| 類型 | 定義位置 | 隱含外部引用 | 可定義 static 成員 | 典型用途 |
| 成員內部類 | 類別內部、方法外 | 是 | 否 | 輔助類、狀態持有、迭代器 |
| 靜態內部類 | 類別內部、方法外 + static | 否 | 是 | 單例 Holder、資料容器、工具類、Builder |
| 局部內部類 | 方法內/程式碼區塊內 | 是 (捕獲 final 區域變數) | 否 | 複雜邏輯封裝、一次性回呼 |
| 匿名內部類 | 運算式中 (new ... {}) | 是 (捕獲 final 區域變數) | 否 | 事件監聽、Runnable、Comparator、一次性實作 |
核心語法:.this 與 .new
public class Outer {
private String name = "Outer";
class Inner {
void print() {
System.out.println(name); // 直接存取外部私有欄位
System.out.println(Outer.this.name); // 顯式引用外部實例
}
}
public void createInner() {
// 建立非靜態內部類實例:需外部實例
Inner inner = new Inner(); // 等同 this.new Inner()
Inner inner2 = Outer.this.new Inner(); // 靜態方法中需外部實例引用
}
static class StaticNested {
void print() {
// System.out.println(name); // 編譯錯誤:無外部實例引用
System.out.println("Static nested");
}
}
public static void createStaticNested() {
StaticNested sn = new StaticNested(); // 無需外部實例
}
}
編譯產物與命名規則
- 編譯後產生獨立
.class 檔案:Outer.class, Outer$Inner.class, Outer$1.class (匿名)。
- 非靜態內部類建構子**隱含參數**
Outer this$0,編譯器自動傳入外部實例引用。
- 內部類**非運行時概念**:JVM 視為獨立類別,特權存取由編譯器生成存取方法 (
access$000) 實現。
各類型深度解析
1. 成員內部類
public class LinkedList {
private Node first;
private class Node { // 內部類泛型獨立於外部類
E item;
Node next;
Node(E item) { this.item = item; }
}
// 迭代器常用內部類
private class ListIterator implements Iterator { ... }
}
- 外部類建議提供工廠方法
createInner() 封裝 new Inner()。
- 不可定義
static 成員 (介面 default/static 方法除外)。
2. 靜態內部類
public class Config {
static class Builder {
String url; int port;
Builder url(String u) { url=u; return this; }
Builder port(int p) { port=p; return this; }
Config build() { return new Config(this); }
}
private final String url; private final int port;
private Config(Builder b) { url=b.url; port=b.port; }
}
- **Builder 模式** 經典應用。
- **單例 Holder** (見 113970)。
- 可定義靜態欄位/方法/巢狀類別。
3. 局部內部類
public void process(List data) {
class Processor { // 僅在方法內可見
String prefix = "PROC: ";
void processItem(String item) { System.out.println(prefix + item); }
}
Processor p = new Processor();
data.forEach(p::processItem);
}
- 可存取方法的 **final/effectively final 區域變數**。
- 不能有存取修飾詞 (private/public 等)。
- 不能定義 static 成員。
4. 匿名內部類
// Swing 事件
button.addActionListener(new ActionListener() {
int count = 0;
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked " + ++count);
}
});
// 執行緒
new Thread(new Runnable() {
public void run() { System.out.println("Running in " + Thread.currentThread().getName()); }
}).start();
- 見 113991 詳細解析。
- Java 8+ 優先用 Lambda 替代 (限函數式介面)。
常見陷阱與最佳實踐
- 記憶體洩漏:非靜態內部類實例持有外部實例引用。長壽命外部物件 (Activity、Cache、單例) 持有短壽命內部類實例 → 外部物件無法 GC。
→ 解決:改用 static 內部類、弱引用、或 Lambda (無外部引用)。
- 序列化陷阱:序列化非靜態內部類實例會**連帶序列化外部實例**。匿名類別合成名稱不穩定跨版本失效。
→ 避免序列化內部類,或實作 Externalizable 手動控制。
- 阻礙 GC 的靜態引用:靜態欄位持有內部類實例 → 內部類持有外部實例 → 整個物件圖無法回收。
- 命名衝突:內部類與外部類同名欄位/方法 → 用
Outer.this.field 區分。
- 建構子參數傳遞:非靜態內部類建構子隱含外部引用,反射建立時需手動提供
constructor.newInstance(outerInstance, ...)。
現代替代方案
- Lambda:取代單一方法匿名類別 (Runnable, Comparator, Listener)。
- 靜態巢狀類別 + 組合:取代非靜態內部類持有外部引用的狀態共享。
- 頂層類別 (package-private):若內部類被多處使用、邏輯獨立,提升為頂層類別更清晰。
- Records (Java 14+):不可變資料載體,常用於靜態內部類替代 Builder/DTO。
- 上一篇文章: Java 實現多重繼承:介面與內部類兩大核心方案
- 下一篇文章: Java 抽象類別與介面深度比較:語法、設計哲學、選擇決策樹
Copyright ©
程式碼之家 All Rights Reserved