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

Thinking in Java 學習筆記(十):類別複用——組合與繼承深度解析

     編輯:Java 相關知識

第六章:重複運用 Classes

面向過程語言靠複製程式碼複用;Java 物件導向有兩大機制:

  1. 組合 Composition:在新類別中使用現有類別物件,複用功能而非形式。
  2. 繼承 Inheritance:新類別成為現有類別的一種,依需求增修,不動原類別。

組合語法與範例


class Composition {
    private String s;
    Composition() {
        System.out.println("Composition()");
        s = new String("hello");
    }
    public String toString() { return s; }
}

public class Test {
    Composition c; // 物件參考
    int i;
    public void show() {
        System.out.println("int = " + i);
        System.out.println("Composition = " + c);
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.show();
    }
}

非基本型別參考預設 null,使用前必須初始化,否則 NullPointerException。每個物件皆有 toString() 供字串轉換。

繼承機制與範例

關鍵字 extends。子類別自動獲得父類別所有成員。所有類別隱式繼承 Object。


class Base {
    int i = 10;
    public void show() { System.out.println("base method"); }
    public static void main(String[] args) { new Base().show(); }
}

class Derived extends Base {
    public void show() { // 覆寫
        System.out.println("derived method");
        super.show(); // 呼叫父類別方法
    }
    public void newMethod() { // 新增方法
        System.out.println(i); // 存取父類別欄位
    }
    public static void main(String[] args) {
        Derived d = new Derived();
        d.show();
        d.newMethod();
    }
}

基底類別初始化順序

  1. 子類別初始化前,系統先初始化父類別。
  2. 編譯器在子類別建構子前隱式插入 super()。

class Base { Base() { System.out.println("base constructor"); } }
class Derived extends Base {
    Derived() {
        // super(); 自動插入
        System.out.println("derived constructor");
    }
    public static void main(String[] args) { new Derived(); }
}

父類別建構子帶參數時

編譯器不會自動呼叫,必須在子類別建構子第一行顯式呼叫 super(參數)。


class Base { Base(String s) { System.out.println(s); } }
class Derived extends Base {
    Derived() {
        super("base constructor"); // 必須第一行
        System.out.println("derived constructor");
    }
}

組合與繼承結合

實務上常同時使用。選擇準則:

  • 僅需功能、隱藏實作細節 → 組合。
  • 需開發特殊版本、擴展行為 → 繼承。
  • is-a (是一個) 關係 → 繼承;has-a (有一個) 關係 → 組合。

protected 關鍵字

子類別可存取,同套件其他類別為 friendly (package-private)。

漸進式開發

繼承支援在不影響父類別程式碼下新增功能。

向上轉型 Upcasting

子類別可當作父類別使用,任何傳給父類別的訊息皆可傳給子類別。


class Base {
    protected void show() { System.out.println("base"); }
    protected void getSomeone(Base b) { b.show(); }
}
class Derived extends Base {
    public static void main(String[] args) {
        Derived d = new Derived();
        d.getSomeone(d); // Derived 傳入 Base 參數合法
    }
}

編譯器看到 Base b 參數,實際傳入 Derived 合法,因子類別是父類別的一種。此轉型稱為向上轉型,安全(特殊→通用)。

向上轉型後的方法呼叫


class Base {
    public void methodOne() { System.out.println("Base.methodOne"); }
    public void methodTwo() { System.out.println("Base.methodTwo"); }
}
class Derived extends Base {
    public void methodOne() { System.out.println("Derived.methodOne"); }
    public void methodTwo() { System.out.println("Derived.methodTwo"); }
    public void methodNew() { System.out.println("Derived.methodNew"); }
    public static void main(String[] args) {
        Base d = new Derived(); // 向上轉型
        d.methodOne(); // 輸出 Derived.methodOne (動態繫結)
        d.methodTwo(); // 輸出 Derived.methodTwo
        // d.methodNew(); // 編譯錯誤:Base 無此方法
    }
}

向上轉型會「遺失」子類別特有方法,但多型確保覆寫方法正確派發。

為什麼需要向上轉型?

子類別是父類別的超集,向上轉型安全,是多型運作基石。

組合 vs 繼承:決策指南

  • 需要向上轉型時 → 繼承。
  • 其餘情況優先考慮組合(彈性較高)。

final 關鍵字深度解析

1. final 資料 (常數)

  • 編譯期常數:基本型別、final、有初值。編譯期可計算,減少執行負擔。
  • 執行期常數:物件參考或基本型但在執行期初始化(如 final int x = (int)(Math.random()*20))。
  • static final:類別常數,唯一儲存空間,不可改變。
  • 參考型別 final:參考不可變,但物件內容可修改。

2. 空白 final

宣告 final 但不立即初始化,建構子中必須初始化。兼具不變性與靈活性(每個實例可有不同常數值)。

3. final 參數

  • 基本型別:值不可改。
  • 參考型別:參考不可改(不可指向新物件)。

4. final 方法

  • 鎖定方法,禁止子類別覆寫。
  • 短小方法可內聯提升效能(現代 JVM 自動優化,手動加 final 效果有限)。

5. final 與 private

private 方法隱性為 final(無法覆寫)。在 private 加 final 無意義。

6. final 類別

禁止被繼承。資料成員可自由選擇 final 與否。

初始化與類別載入

  • Java 採延遲載入:首次使用 (建立物件、存取 static 成員) 才載入。
  • 載入順序:父類別 static → 子類別 static → 父類別 instance → 父類別建構子 → 子類別 instance → 子類別建構子。

繼承與初始化順序示範


class Base {
    int i = 10; int j;
    Base() { show("i=" + i + ", j=" + j); j = 20; }
    static int m = show("static Base.m init");
    static int show(String s) { System.out.println(s); return 30; }
}
class Derived extends Base {
    int k = show("Derived.k init");
    Derived() { show("k=" + k); show("j=" + j); }
    static int n = show("static Derived.n init");
    public static void main(String[] args) {
        show("Derived main");
        new Derived();
    }
}

輸出

static Base.m init
static Derived.n init
Derived main
i=10, j=0
Derived.k init
k=30
j=20

總結

組合複用實作,繼承複用介面。組合彈性高,繼承支援向上轉型與多型。建議多用組合,必要時繼承。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved