內部類是 Java 1.1 引入的特性,允許在類別內部定義另一個類別。它提供了封裝性、存取外部成員的便利性,並能實現多重繼承的變相效果。本文系統整理四大類型、實例化語法、作用域規則與實戰注意事項。
| 類型 | 定義位置 | 修飾符 | 存取外部成員 | 實例化方式 | 典型用途 |
|---|---|---|---|---|---|
| 成員內部類 | 類別內部、方法外部 | public/protected/private/static/abstract/final | 直接存取(含 private) | outer.new Inner() | 邏輯強相關、需頻繁存取外部狀態 |
| 靜態內部類 | 類別內部、方法外部,加 static | 同上,但 static 修飾 | 僅能存取外部 static 成員 | new Outer.StaticInner() | 工具類、Builder 模式、單例持有者、不依賴外部實例的邏輯 |
| 局部內部類 | 方法內部、程式碼區塊內 | 無修飾符(預設 package-private) | 存取外部 final/效果上 final 變數 | 在定義處直接 new | 邏輯極其局部、一次性使用、實作介面回傳 |
| 匿名內部類 | 表達式中(new 後) | 無名稱,隱含 final | 同局部內部類 | new Interface() { ... } 或 new Parent() { ... } | 一次性實作介面/抽象類、事件監聽器、執行緒任務 |
public class Outer {
private int outerField = 10;
public class Inner {
public void print() {
System.out.println(outerField); // 直接存取
System.out.println(Outer.this.outerField); // 顯式限定
}
}
}
// 外部類別實例化
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // 必須先有外部實例
inner.print();
public class Outer {
private static int staticField = 20;
static class StaticInner {
public void print() {
System.out.println(staticField); // 僅能存取 static
}
}
}
// 無需外部實例
Outer.StaticInner si = new Outer.StaticInner();
si.print();
// 傳統寫法
Runnable task = new Runnable() {
@Override public void run() { System.out.println("Running..."); }
};
new Thread(task).start();
// Java 8+ Lambda 替代(僅限函數式介面)
new Thread(() -> System.out.println("Running...")).start();
Outer.this 參考。static 內部類;或用 WeakReference<Outer>;或在外部類銷毀時清理內部實例引用。
public class Outer {
int x = 1;
class Inner {
int x = 2;
void test(int x) { // 區域變數 x=3
System.out.println(x); // 3
System.out.println(this.x); // 2
System.out.println(Outer.this.x); // 1
}
}
}
非 static 內部類實作 Serializable 會連帶序列化外部類實例,導致序列化體積暴增、反序列化脆弱。嚴禁序列化非 static 內部類。
Outer$Inner.class。Outer$Inner(Outer)。access$000)實現,間接降低效能(極微)。static 避免隱含引用。
public class HttpRequest {
private final String url;
private final Map<String, String> headers;
private HttpRequest(Builder b) { this.url = b.url; this.headers = b.headers; }
public static class Builder { // static 內部類
private String url; private Map<String, String> headers = new HashMap<>();
public Builder url(String u) { this.url = u; return this; }
public Builder header(String k, String v) { headers.put(k, v); return this; }
public HttpRequest build() { return new HttpRequest(this); }
}
}
// 使用
HttpRequest req = new HttpRequest.Builder().url("/api").header("k","v").build();
內部類是 Java 封裝與回調的利器,但濫用會增加認知負擔與記憶體風險。掌握四大類型差異、static 關鍵字效力、Lambda 替代時機,才能寫出既優雅又安全的代碼。