CodeGym /課程 /JAVA 25 SELF /抽象類別與方法:語法與範例

抽象類別與方法:語法與範例

JAVA 25 SELF
等級 19 , 課堂 1
開放

1. 抽象類別的語法

先來弄清楚,什麼是 Java 的抽象類別,以及為什麼需要它。想像你有一組 LEGO:你有一堆零件(例如「輪子」、「積木」),也有某些特定模型才會用到的零件。抽象類別就像是一份操作零件的通用說明書,而不是某個具體模型的組裝步驟:積木如何卡榫、輪子如何裝在軸上、零件可以一層一層堆疊。抽象類別描述的是這些通用規則與必要步驟,但不細寫具體模型的組裝。具體的模型就是子類別:它們拿著通用說明,再補上缺少的步驟。

在 Java 中,抽象類別是一種無法直接被建立實例的類別(不能寫 new AbstractClass()),但可以被繼承,並在子類別中實作它「尚未完成」的方法。

抽象類別適用於以下情況:

  • 一群物件擁有共同的行為或狀態,但部分邏輯各自不同。
  • 你想先提供部分「預設」功能,剩下的留給子類別去實作。
  • 你想禁止直接建立此類別的物件(例如,「動物」本身不會直接存在,但「貓」就會)。

如何宣告抽象類別

很簡單:在 class 前面加上關鍵字 abstract

public abstract class Animal {
    // 欄位(例如,動物的名字)
    protected String name;

    // 建構子
    public Animal(String name) {
        this.name = name;
    }

    // 抽象方法——只有宣告,沒有實作!
    public abstract void makeSound();

    // 一般(已有實作)的方法
    public void sleep() {
        System.out.println(name + " 睡覺:Zzz...");
    }
}

特點:

  • 抽象類別可以同時包含已有實作的方法與抽象方法。
  • 抽象類別可以包含欄位、建構子,甚至 static 方法。
  • 不能直接建立抽象類別的物件:
  • Animal a = new Animal("某個生物"); // 錯誤!

如何宣告抽象方法

抽象方法就是沒有方法本體的方法,也就是沒有大括號與內部程式碼。它用關鍵字 abstract 宣告,並且一定要以分號 ; 結尾。

public abstract void makeSound();
  • 抽象方法只能在抽象類別內宣告。
  • 繼承抽象類別的類別必須實作所有抽象方法,否則它本身也必須標記為 abstract

2. 繼承抽象類別:運作方式

來看一個具體範例。假設有抽象類別 Animal 與抽象方法 makeSound()。現在建立子類別 Dog,並實作該方法:

public class Dog extends Animal {
    public Dog(String name) {
        super(name); // 呼叫基底類別的建構子
    }

    @Override
    public void makeSound() {
        System.out.println(name + " 在叫:汪汪!");
    }
}

再看另一個子類別:

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println(name + " 喵叫:喵!");
    }
}

現在我們可以在程式中使用這些類別:

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("小球");
        Animal cat = new Cat("穆爾卡");

        dog.makeSound(); // 小球 在叫:汪汪!
        cat.makeSound(); // 穆爾卡 喵叫:喵!

        dog.sleep(); // 小球 睡覺:Zzz...
        cat.sleep(); // 穆爾卡 睡覺:Zzz...
    }
}

請注意:
我們可以宣告型別為 Animal 的變數,但真正建立的物件必須是具體(非抽象)的子類別。

示意圖:關係長什麼樣


.         Animal (abstract)
            /         \
         Dog         Cat
 (實作 makeSound) (實作 makeSound)

3. 什麼時候用抽象類別,什麼時候用介面?

這是面試中最常見的問題之一!我們來理清一下:

  • 抽象類別:當物件之間有共同狀態(例如欄位)、共同邏輯(有實作的方法),而你想提供一個可再擴充的「行為骨架」。
  • 介面:當你只想定義一組方法(契約),不包含實作與狀態。自 Java 8 起,介面有了 default/static 方法,但它的本質仍是描述「物件該能做什麼」,而不是「怎麼做」。

生活範例:
「鳥」是抽象類別:所有鳥都有喙、翅膀,而且都會飛(但方式不同)。
「會飛的」是介面:不只鳥會飛,飛機、超級英雄也會!大家的飛法不同,但重點是「具備會飛的能力」。

4. 實用範例

範例 1:含部分實作的抽象類別

假設你在做一款遊戲,裡面有不同種類的交通工具。它們都可以移動,但方式各異。不過它們都有速度、名稱,以及一種標準的停止方式。

public abstract class Transport {
    protected String name;
    protected int speed;

    public Transport(String name, int speed) {
        this.name = name;
        this.speed = speed;
    }

    // 抽象方法——由子類別實作
    public abstract void move();

    // 已實作的方法
    public void stop() {
        System.out.println(name + " 停下來了。");
    }
}

public class Car extends Transport {
    public Car(String name, int speed) {
        super(name, speed);
    }

    @Override
    public void move() {
        System.out.println(name + " 以 " + speed + " 公里/小時 在道路上行駛。");
    }
}

public class Bicycle extends Transport {
    public Bicycle(String name, int speed) {
        super(name, speed);
    }

    @Override
    public void move() {
        System.out.println(name + " 以 " + speed + " 公里/小時 踩踏板。");
    }
}

使用方式:

Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);

car.move(); // Toyota 以 120 公里/小時 在道路上行駛。
bike.move(); // Stels 以 25 公里/小時 踩踏板。

car.stop(); // Toyota 停下來了。
bike.stop(); // Stels 停下來了。

範例 2:帶參數的抽象方法

public abstract class Shape {
    public abstract double area();
}

public class Circle extends Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    @Override
    public double area() {
        return width * height;
    }
}

使用方式:

Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);

System.out.println("圓的面積:" + c.area());
System.out.println("長方形的面積:" + r.area());

5. 抽象類別的特性與注意事項

  • 抽象類別可以使用任何存取修飾子:publicprotectedprivate(例如用於欄位)。
  • 可以宣告沒有任何抽象方法的抽象類別。如果你只是想禁止建立基底類別的實例,這會很有用。
  • 若類別繼承了抽象類別卻沒有實作所有抽象方法,它本身也必須宣告為 abstract
  • 抽象類別可以有建構子。建立子類別物件時會呼叫它們(經由 super(...))。
  • 抽象方法不能是 private(否則無法在子類別中實作)。
  • 抽象類別可以包含 static 方法與欄位。
  • 抽象類別可以實作介面,但不一定要在抽象類別中實作介面的方法——也可以交由子類別實作。

表格:抽象類別 vs 介面

抽象類別 介面
關鍵字
abstract class
interface
是否可包含欄位 是(任意) 自 Java 8 起——僅 static/final
是否可有已實作的方法 自 Java 8 起——default/static
抽象方法
多重繼承 是(可實作多個介面)
建構子 沒有
可否建立物件

6. 使用抽象類別與方法時的常見錯誤

錯誤 1:嘗試建立抽象類別的實例。
如果你寫了 new Animal("某個生物"),編譯器會立刻提醒抽象類別並非為此而設。記住:抽象就像「骨架」,不是「活生生的個體」。

錯誤 2:在子類別中漏掉某些抽象方法的實作。
如果你的類別少實作了基底抽象類別中的任一抽象方法,它本身就必須宣告為 abstract,否則會得到編譯錯誤。

錯誤 3:在非抽象類別中宣告抽象方法。
在 Java 中,不能在一般(非抽象)類別中宣告抽象方法——編譯器會立刻報錯。

錯誤 4:將抽象方法宣告為 privatestatic
抽象方法不能是 private(不然無法被覆寫),也不能是 static(因為靜態方法不會被覆寫)。當然也不能是 final,因為 final 會禁止覆寫。

錯誤 5:忘了替抽象方法指定適當的存取修飾子。
如果你沒有明確指定修飾子,方法會是封包層級可見(package-private),有時這並非你想要的行為。

1
任務
JAVA 25 SELF, 等級 19, 課堂 1
上鎖
又一位數位動物園的居民 🐾
又一位數位動物園的居民 🐾
1
任務
JAVA 25 SELF, 等級 19, 課堂 1
上鎖
智慧家庭控制系統 📱
智慧家庭控制系統 📱
1
任務
JAVA 25 SELF, 等級 19, 課堂 1
上鎖
幾何編輯器:圖形及其面積 📐
幾何編輯器:圖形及其面積 📐
1
任務
JAVA 25 SELF, 等級 19, 課堂 1
上鎖
IT 公司的員工管理系統 💼
IT 公司的員工管理系統 💼
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION