首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 內部類完整教學:四大類型、.this/.new 語法、編譯產物、最佳實踐

     編輯:Java 相關知識

為什麼需要內部類?

  • 邏輯分組:僅在外部類內部使用的輔助類別,封裝在內部不污染頂層命名空間。
  • 存取外部私有成員:內部類可**無限制存取**外部類所有成員 (含 private),外部類亦可存取內部類私有成員。
  • 實現多重繼承變體**:每個內部類可獨立繼承不同類別/實作不同介面 (見 113993)。
  • 回呼/事件監聽器:隱含持有外部引用,天然適合回呼情境。

四大類型總覽

核心語法:.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 替代 (限函數式介面)。

常見陷阱與最佳實踐

  1. 記憶體洩漏:非靜態內部類實例持有外部實例引用。長壽命外部物件 (Activity、Cache、單例) 持有短壽命內部類實例 → 外部物件無法 GC。
    → 解決:改用 static 內部類、弱引用、或 Lambda (無外部引用)。
  2. 序列化陷阱:序列化非靜態內部類實例會**連帶序列化外部實例**。匿名類別合成名稱不穩定跨版本失效。
    → 避免序列化內部類,或實作 Externalizable 手動控制。
  3. 阻礙 GC 的靜態引用:靜態欄位持有內部類實例 → 內部類持有外部實例 → 整個物件圖無法回收。
  4. 命名衝突:內部類與外部類同名欄位/方法 → 用 Outer.this.field 區分。
  5. 建構子參數傳遞:非靜態內部類建構子隱含外部引用,反射建立時需手動提供 constructor.newInstance(outerInstance, ...)。

現代替代方案

  • Lambda:取代單一方法匿名類別 (Runnable, Comparator, Listener)。
  • 靜態巢狀類別 + 組合:取代非靜態內部類持有外部引用的狀態共享。
  • 頂層類別 (package-private):若內部類被多處使用、邏輯獨立,提升為頂層類別更清晰。
  • Records (Java 14+):不可變資料載體,常用於靜態內部類替代 Builder/DTO。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型定義位置隱含外部引用可定義 static 成員典型用途
成員內部類類別內部、方法外是否輔助類、狀態持有、迭代器
靜態內部類類別內部、方法外 + static否是單例 Holder、資料容器、工具類、Builder
局部內部類方法內/程式碼區塊內是 (捕獲 final 區域變數)否複雜邏輯封裝、一次性回呼
匿名內部類運算式中 (new ... {})是 (捕獲 final 區域變數)否事件監聽、Runnable、Comparator、一次性實作