CodeGym /課程 /JAVA 25 SELF /繼承的概念,extends 語法

繼承的概念,extends 語法

JAVA 25 SELF
等級 17 , 課堂 0
開放

1. 什麼是繼承?

繼承是一種機制,能夠在既有類別的基礎上建立新的類別,並「繼承」其屬性與方法。新的類別稱為子類別(或衍生類別),原本的類別稱為父類別(或基底類別)。

這就像是你用舊食譜做新蛋糕:把原先的精華都保留,再加上幾樣新食材——瞧,已經有一款「奶奶的祕密配方蛋糕」了。

  • 父類別——包含通用的屬性與行為。
  • 子類別——繼承這些內容,並可再加入自己的東西或改變行為。

例如:「貓是一種動物」:所有的貓都是動物,但不是所有動物都是貓。動物可以作為基底類別,貓則是它的子類別;狗也一樣。
再如:「圓形是一種圖形」:每個圓形都是圖形,但不是每個圖形都是圓形。圖形可作為父類別,而圓形與三角形則是其子類別。在 Java 中,這些關係是透過關鍵字 extends 來實作的。

為什麼需要繼承?

價值百萬的問題:為什麼不能直接把某個類別的程式碼複製到另一個類別?為什麼還要發明繼承?

  • 重複使用程式碼:所有共通的部分都抽到基底類別。無須重複宣告欄位與方法——它們會自動出現在子類別中。比如在大學系統中,可以有一個基底類別「人」,至少包含姓名與生日,而「學生」與「教師」則是子類別,各自有自己的屬性。
  • 擴充與專門化:可以僅在需要的地方新增屬性與方法,或覆寫既有行為。所有動物都會發出聲音,但貓會喵喵叫、狗會汪汪叫。同時,你也可以只在貓這個類別加上「爬樹」的能力(基底類別沒有這項能力)。
  • 階層化結構:程式碼更有邏輯、也更容易理解。例如有 AnimalDogCat 這幾個類別,就能一眼看出 DogCatAnimal 的特例。

範例。如果 Java 沒有繼承,你就得在每個動物類別裡都複製像是 eat()sleep() 這些方法,以及 name 欄位。要是明天想新增 breathe() 方法,就得修改數十個類別!有了繼承——只要在基底類別中加入「呼吸」的能力即可。

2. 繼承的語法:extends

如何在 Java 中宣告繼承?很簡單:使用關鍵字 extends

class RoditelskiyKlass {
    // 欄位與方法
}

class Podklass extends RoditelskiyKlass {
    // 額外的欄位與方法
}

範例 1:動物

// 父類別
class Animal {
    String name;

    void eat() {
        System.out.println(name + " 正在吃。");
    }
}

// 子類別
class Cat extends Animal {
    void meow() {
        System.out.println(name + " 說:喵!");
    }
}

這裡發生了什麼?

  • Cat 類別繼承了 Animal 類別的所有欄位與方法。
  • 因此 Cat 已經擁有 name 欄位與 eat() 方法,即使我們沒有顯式寫出來。
  • 另外 Cat 也新增了自己的方法 meow()

使用方式

public class Main {
    public static void main(String[] args) {
        Cat kitty = new Cat();
        kitty.name = "巴爾西克";
        kitty.eat();      // 巴爾西克 正在吃。  (繼承而來)
        kitty.meow();     // 巴爾西克 說:喵! (自有方法)
    }
}

大功告成!我們用最少的程式碼得到一個新類別,避免了重複。

3. 子類別究竟繼承了什麼?

在 Java 中子類別會繼承:

  • 所有公開(public)與受保護(protected)的欄位與方法(來自基底類別)。
  • 所有 package-private 的欄位與方法(若子類別位於同一個 package)。

不會繼承:

  • 建構子(需要在子類別中顯式宣告)。
  • 私有(private)的欄位與方法(它們只在基底類別內可見)。
  • 靜態初始化區塊(不會被繼承,但每個類別都可以有自己的)。
  • package-private 成員,如果子類別位於不同的 package。

示意

class Animal {
    public String name;
    private int age;

    public void eat() { 
        System.out.println("在吃"); 
    }
    private void secret() { 
        System.out.println("祕密!"); 
    }
}

class Dog extends Animal {
    public void bark() { 
        System.out.println("汪!"); 
    }
}
  • Dog 會繼承 name 欄位與 eat() 方法。
  • Dog 看不到 age 欄位與 secret() 方法——因為它們是 private。

4. Java 中繼承的限制

Java 相當嚴格,也有自己的規則:

只能單一繼承類別:每個類別只能繼承自一個其他類別。不過,一個類別可以有任意多個子類別。

class Dog extends Animal { ... } // OK
class Dog extends Animal, Pet { ... } // 錯誤!不能這樣

介面可以多重繼承——可以,但這是另一個主題,我們之後再談。

Object 類別——是 Java 中所有類別的祖先。即使你沒有明寫 extends,你的類別也會隱式地繼承 Object

5. 實用範例

讓我們繼續構建我們的「動物學」應用。

步驟 1. 基底類別 Animal

class Animal {
    String name;

    void eat() {
        System.out.println(name + " 正在吃。");
    }
}

步驟 2. 子類別 Cat

class Cat extends Animal {
    void meow() {
        System.out.println(name + " 說:喵!");
    }
}

步驟 3. 子類別 Dog

class Dog extends Animal {
    void bark() {
        System.out.println(name + " 說:汪!");
    }
}

步驟 4. 使用這些類別

public class Main {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "穆爾卡";
        cat.eat();   // 穆爾卡 正在吃。
        cat.meow();  // 穆爾卡 說:喵!

        Dog dog = new Dog();
        dog.name = "沙里克";
        dog.eat();   // 沙里克 正在吃。
        dog.bark();  // 沙里克 說:汪!
    }
}

我們得到了什麼?

  • CatDog 這兩個類別都使用了基底類別中的共用欄位 name 與方法 eat()
  • 每個類別都加入了各自的獨特行為:貓有 meow(),狗有 bark()
  • 如果需要改變動物進食的方式,只要修改 Animal 類別中的 eat() 方法——變更會自動套用到所有子類別。

6. 實用的小細節

視覺化:類別階層

可以這樣想像階層:

.        Animal
        /    \
     Cat     Dog
  • Animal——基底類別,包含動物的共通部分。
  • CatDog——子類別,加入各自的特點。

談談建構子與繼承

建構子不會被繼承。如果你沒有在子類別中定義建構子,而父類別有無參數建構子,Java 會自動加入;若父類別只有帶參數的建構子,則必須在子類別中透過 super(...) 明確呼叫。簡短範例:

class Animal {
    String name;
    Animal(String name) {
        this.name = name;
    }
}

class Cat extends Animal {
    Cat(String name) {
        super(name); // 呼叫父類別的建構子
    }
}

7. 使用繼承時的常見錯誤

錯誤 1:試圖同時繼承多個類別。
在 Java 中不能寫 class Cat extends Animal, Pet。只有一個父類別!

錯誤 2:以為 private 欄位與方法在子類別中也可見。
private 成員不會被存取(更精確地說,它們存在,但在子類別中無法存取)。如果要讓子類別能存取,請使用 protected

錯誤 3:忘了呼叫父類別建構子。
如果基底類別沒有無參數建構子,而你又沒有顯式呼叫 super(...),就會出現編譯錯誤。

錯誤 4:為了「省程式碼」而使用繼承,卻不符合語意。
如果類別之間不存在「is-a」關係(is-a),最好不要使用繼承。例如,「人」不應該繼承自「汽車」,即使它們都有 speed 欄位。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION