內部類別是定義在另一個類別內部的類別。雖然編譯後仍會產生獨立的 .class 檔案(如 Outer$Inner.class),但內部類別提供了更強的封裝性、便於存取外部類別私有成員、且程式碼組織更緊湊。Java 提供四種內部類別,各有語法、生命週期與適用情境。
static 修飾,定義在外部類別成員位置。public class Outer {
private static int shared = 100;
public static class StaticInner {
public void innerMethod() {
System.out.println("inner " + shared); // 僅能存取 static shared
}
}
public void test() {
StaticInner si = new StaticInner();
si.innerMethod();
}
}
// 外部使用:new Outer.StaticInner()
Outer、Outer$StaticInner。private static 欄位時,編譯器在外部類別生成 package-private 存取方法(如 static int access$0() { return shared; }),內部類別呼叫此方法。Integer.IntegerCache、LinkedList.Node、Character.UnicodeBlock。Outer.this.成員 明確引用外部實例(重名時必要)。Outer.Inner inner = new Outer().new Inner();public class Outer {
private int a = 100;
public class Inner {
public void innerMethod() {
System.out.println("outer a " + a); // 直接存取外部實例欄位
Outer.this.action(); // 明確引用外部方法
}
}
private void action() { System.out.println("action"); }
}
// 外部使用:Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
Outer、Outer$Inner。Outer$Inner 持有 final Outer outer 參照,建構子接收外部實例。access$0(Outer)、access$1(Outer) 等存取方法供內部類別呼叫私有成員。LinkedList.ListItr 實作 Iterator)。private 完全封裝。public class Outer {
private int a = 100;
public void test(final int param) {
final String str = "hello";
class Inner {
public void innerMethod() {
System.out.println(a + ", " + param + ", " + str);
}
}
new Inner().innerMethod();
}
}
編譯器將參數/區域變數值複製到內部類別實例欄位中(如 OuterInner(Outer outer, int param))。若外部修改原變數,內部類別持有的副本不會更新,導致不一致。強制 final 確保值不會變更,消除困惑。
變通方案:若確需修改外部變數,改用單元素陣列 final String[] holder = {"hello"};,修改 holder[0] 內容。
new 運算子後直接定義並建立實例。new 父類別(參數) { ... } 或 new 介面() { ... }。public class Outer {
public void test(final int x, final int y) {
Point p = new Point(2, 3) { // 匿名內部類別繼承 Point
@Override public double distance() {
return distance(new Point(x, y)); // 捕獲 x, y
}
};
System.out.println(p.distance());
}
}
Outer、Outer$1(編號遞增)。Outer$1 建構子接收外部實例、父類別建構子參數、捕獲的區域變數。Arrays.sort(arr, new Comparator() { ... })button.addActionListener(new ActionListener() { ... })new Thread(new Runnable() { ... })本質上是針對介面編程與回呼模式的輕量實作:將固定框架(排序演算法、事件分派)與可變邏輯(比較規則、事件處理)分離,透過匿名類別注入具體行為。
| 類型 | 定義位置 | 存取外部實例成員 | 建立實例語法 | 可定義 static 成員 | 典型用途 |
|---|---|---|---|---|---|
| 靜態內部類別 | 類別成員層級 | ❌ 僅 static | new Outer.StaticInner() | ✅ | 快取、節點、工具類 |
| 成員內部類別 | 類別成員層級 | ✅ 全部 | outer.new Inner() | ❌ (final 除外) | 迭代器、狀態相關實作 |
| 方法內部類別 | 方法內部 | 視方法而定 | 方法內 new Inner() | ❌ | 極局部邏輯封裝 |
| 匿名內部類別 | 運算式中 | ✅ 全部 + final 區域變數 | new Parent() {} | ❌ | 介面回呼、一次性實作 |
static 避免隱含參照導致記憶體洩漏(外部實例無法被 GC)。NotSerializableException 或資料洩漏。