內部類是 Java 中一項強大卻常被誤解的特性。本文通過五個遞進式實例,系統講解成員內部類、靜態內部類、私有內部類、方法局部內部類及匿名內部類的語法結構、訪問權限、編譯產物與典型應用場景,助你徹底掌握這項核心語法。
內部類定義在另一個類的內部,邏輯上隸屬於外部類。它能無條件訪問外部類所有成員 (含 private),這是其存在的唯一核心優勢——如同心臟直接泵血而無需醫生抽血。
編譯後生成獨立 .class 文件,命名規則:OuterClass$InnerClass.class,`$` 符號代表層級關系。
class Out {
private int age = 12;
class In {
public void print() { System.out.println(age); }
}
}
public class Demo {
public static void main(String[] args) {
Out.In in = new Out().new In(); // 必須先有外部類實例
in.print(); // 輸出 12
}
}
關鍵點:
OuterClass.InnerClass obj = new OuterClass().new InnerClass();OuterClass.this),故不能在靜態上下文直接實例化非靜態內部類。class Out {
private int age = 12;
class In {
private int age = 13;
public void print() {
int age = 14;
System.out.println("局部變量:" + age); // 14
System.out.println("內部類變量:" + this.age); // 13
System.out.println("外部類變量:" + Out.this.age); // 12
}
}
}
訪問規則口訣:局部 > 內部類成員 > 外部類成員。同名時需用 this 或 OuterClass.this 顯式限定。
class Out {
private static int age = 12;
static class In {
public void print() { System.out.println(age); }
}
}
public class Demo {
public static void main(String[] args) {
Out.In in = new Out.In(); // 無需外部類實例
in.print(); // 12
}
}
特性:
OuterClass.InnerClass 實例化,行為類似頂層類,常用於工具類、Builder 模式、單例模式靜態內部類寫法。class Out {
private int age = 12;
private class In { // 僅外部類可見
public void print() { System.out.println(age); }
}
public void outPrint() { new In().print(); }
}
public class Demo {
public static void main(String[] args) {
Out out = new Out();
out.outPrint(); // 12
// new Out().new In(); // 編譯錯誤:In 為 private
}
}
用途:完全封裝實現細節,外部不可直接實例化,只能通過外部類提供的方法間接使用,類似「心臟只能由身體控制」。
class Out {
private int age = 12;
public void print(final int x) {
class In { // 定義在方法內
public void inPrint() {
System.out.println(x); // 必須 final 或 effectively final
System.out.println(age);
}
}
new In().inPrint();
}
}
public class Demo {
public static void main(String[] args) {
new Out().print(3); // 輸出 3, 12
}
}
限制:
final (Java 8+ 允許 effectively final) 的局部變量。無名稱、繼承類/實現接口、現場創建實例:
new Thread(new Runnable() {
@Override public void run() { System.out.println("Running..."); }
}).start();
Java 8+ 推薦用 Lambda 替代函數式接口匿名類,代碼更簡潔。
| 類型 | 修飾符 | 訪問外部實例成員 | 需外部實例 | 典型場景 |
|---|---|---|---|---|
| 成員內部類 | 可加 private/protected | 是 (含非 static) | 是 | 邏輯強綁定、迭代器、事件監聽 |
| 靜態內部類 | static | 僅 static 成員 | 否 | 工具類、Builder、單例、緩存節點 |
| 方法局部類 | 無 (隱式 final) | 是 (含非 static) | 是 | 一次性邏輯、線程任務、比較器 |
| 匿名內部類 | 無 | 是 (含非 static) | 是 | 函數式接口實現、事件回調 (Java 7-) |
合理使用內部類可提高封裝性、減少類文件數量、實現閉包效果。但濫用會導致代碼難讀、持有外部引用引發內存洩漏 (如長生命周期匿名類持有 Activity)。原則:優先考慮靜態內部類,其次成員內部類,慎用局部/匿名類,必要時改用 Lambda 或獨立頂層類。