在 Java 程式設計中,內部類是一項強大且靈活的機制,允許將一個類定義在另一個類的內部。這種結構不僅能實現更好的封裝與隱藏,還能讓內部類直接存取外部類的所有成員(包含私有成員),為解決複雜設計問題(如間接實現多重繼承)提供了優雅的方案。本文將系統性地剖析四種核心內部類型:成員內部類、靜態內部類、匿名內部類與局部內部類,並透過完整程式碼範例說明其特性、限制與實際應用場景。
內部類的定義形式如下:
public class Outer {
class Inner {
// 內部類實作
}
}
其四大核心特性:
private、protected 等修飾符,實現真正的隱藏。private 成員。成員內部類屬於外部類的實例成員,可擁有 public、private、protected、預設四種權限。在內部類中存取外部類成員時,需使用 外部類名.this.成員 語法。
外部類名.內部類名 實例名 = 外部類實例.new 內部類建構子();
static 成員(屬性、方法、類),static final 常數除外。因為成員內部類實例必須關聯外部類實例,靜態成員應移至外部類。public class MemberInnerClass {
public static void main(String[] args) {
Outer1 outer = new Outer1();
Outer1.Inner1 inner = outer.new Inner1();
inner.innerShow();
System.out.println("--------------外部類訪問內部類對象--------------");
outer.outerShow();
}
}
class Outer1 {
private String name = "張三";
private int num1 = 10;
public void outerShow() {
System.out.println(name);
System.out.println(num1);
// 外部類需建立內部類物件才能存取
Inner1 inner = new Inner1();
inner.innerShow();
}
public class Inner1 {
private String name = "李四";
private int num2 = 20;
private static final int num3 = 30; // 僅允許 static final 常數
public void innerShow() {
System.out.println(name);
System.out.println(num2);
// 存取外部類成員
System.out.println(Outer1.this.name);
System.out.println(Outer1.this.num1);
}
}
}
class C {
private class A1 extends A { public void showA() { super.showA(); } }
private class B1 extends B { public void showB() { super.showB(); } }
public void showA() { new A1().showA(); }
public void showB() { new B1().showB(); }
}
class Son extends Parent {
public void show() { System.out.println("抽象類中的show方法"); }
private class Inner2 implements IShow {
public void show() { System.out.println("介面中的show方法"); }
}
public void show2() { new Inner2().show(); }
}
使用 static 修飾的成員內部類稱為靜態內部類。它不依賴外部類實例,可視為與外部類平級的獨立類,實例化語法為:
外部類名.內部類名 實例名 = new 外部類名.內部類名();
public class StaticInnerClass {
public static void main(String[] args) {
Outer2.Inner2 inner = new Outer2.Inner2();
inner.innerShow();
Outer2 outer = new Outer2();
outer.outerShow();
}
}
class Outer2 {
private String name = "張三";
private int num1 = 10;
private static int num3 = 100;
public void outerShow() {
System.out.println(name);
System.out.println(num1);
Inner2 inner = new Inner2();
System.out.println(inner.name); // 存取非靜態需實例
System.out.println(Inner2.num3); // 存取靜態直接用類名
}
public static class Inner2 {
private String name = "李四";
private int num2 = 20;
private static final int num3 = 30;
public void innerShow() {
System.out.println(name);
System.out.println(num2);
// System.out.println(Outer2.this.name); // 編譯錯誤:無法存取非靜態
System.out.println(Outer2.num3); // 僅能存取外部類靜態成員
}
}
}
new 敘述中同時宣告並建立實例。{ },但不可定義靜態區塊 static { }。new 介面/父類() {
// 類別主體:實作方法、定義欄位、初始化區塊
}
public class AnonymousInnerClass {
public static void main(String[] args) {
Person person = new Person();
// 直接以匿名類實作 IAnimal 介面
person.feed(new IAnimal() {
private String name = "aa";
{ name = "哈哈"; } // 實例初始化區塊
public void eat() { System.out.println("啃骨頭"); }
public void show() { System.out.println(name); }
});
// 另一種寫法:建立實例後呼叫自有方法
new IAnimal() {
public void eat() { System.out.println("吃魚"); }
public void show() { System.out.println("匿名類自有方法"); }
}.show();
}
}
interface IAnimal { void eat(); }
class Person { public void feed(IAnimal animal) { animal.eat(); } }
外部類名.this.成員。public class LocalInnerClass {
public static void main(String[] args) {
Outer3 outer = new Outer3();
outer.showOuter();
}
}
class Outer3 {
private String name = "張三";
private int num1 = 10;
private static int num2 = 20;
public void showOuter() {
final int num4 = 50; // Java 8 前需显式 final,之後隱含
class Inner3 {
private int num3 = 30;
private int num1 = 20; // 遮蔽外部同名變數
public void showInner() {
System.out.println(num3);
System.out.println(num1); // 內部類自身欄位
System.out.println(Outer3.this.num1); // 外部類實例欄位
System.out.println(Outer3.num2); // 外部類靜態欄位
System.out.println(num4); // 方法區域 final 變數
}
}
Inner3 inner = new Inner3();
inner.showInner();
}
}
| 類型 | 適用場景 | 關鍵特徵 |
|---|---|---|
| 成員內部類 | 需長期關聯外部實例、存取私有成員、模擬多重繼承 | 依賴外部實例、可有權限修飾符、不可有 static 成員 |
| 靜態內部類 | 邏輯屬於外部類但無需存取非靜態成員、工具類、Builder 模式 | 獨立於外部實例、只能存取外部 static 成員 |
| 匿名內部類 | 一次性實作介面/繼承類、事件監聽、執行緒建立、Stream 操作 | 無名、即用即建、可有初始化區塊、無 static 區塊 |
| 局部內部類 | 方法內邏輯複雜需封裝、僅在特定作用域使用 | 定義在區塊/方法內、無修飾符、捕獲 final 區域變數 |
掌握這四種內部類的差異與適用場景,能讓你在設計 Java 類別結構時更靈活地處理封裝、繼承與回呼等需求,寫出更簡潔、可維護的程式碼。