繼承的實現:代碼複用的核心機制

當你需要為貓、狗、鳥等動物類編寫代碼時,是否發現它們都有顏色、叫聲等共同特徵?如果每個類都重複定義這些屬性和方法,不僅冗餘還難以維護。繼承就是為解決這類問題而生的——它允許子類"複用"父類的代碼,並在此基礎上添加新功能。

繼承的基本語法

在 Java 中使用 extends 關鍵字實現繼承,語法格式如下:

複製

// 父類(基類)
public class Animal {
    // 共同屬性
    private String color;

    // 共同方法
    public void shout() {
        System.out.println("動物發出叫聲");
    }

    // getter和setter方法
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }
}

// 子類(派生類)
public class Dog extends Animal {
    // 子類特有方法
    public void wagTail() {
        System.out.println("狗搖尾巴");
    }
}

上述代碼中,Dog 類通過 extends Animal 繼承了 Animal 類的 color 屬性和 shout() 方法,同時新增了 wagTail() 方法。這種結構就像現實中的"狗是動物的一種"關係。

類繼承關係圖

Java 只支持單繼承(一個子類只能有一個直接父類),但支持多層繼承。以下是常見的繼承類型示意圖:

Java21天學習計劃 - 第六天:面向對象基礎之繼承與多態入門_System

關鍵點

  • 單級繼承:如 Dog extends Animal
  • 多級繼承:如 Puppy extends Dog(小狗是狗的一種)
  • 分層繼承:如 Cat 和 Dog 都繼承 Animal
  • ❌ Java 不支持多重繼承(一個類不能同時繼承多個父類)

繼承的好處

  1. 代碼複用:無需重複編寫父類已有的代碼
  2. 擴展性:子類可在父類基礎上添加新功能
  3. 維護性:修改父類代碼,所有子類自動受益

方法重寫與重載:多態的實現基礎

繼承讓子類獲得了父類的方法,但有時需要修改這些方法的行為(如狗和貓的叫聲不同),這就需要方法重寫;而方法重載則是在同一個類中定義多個同名但參數不同的方法。

方法重寫(Override)

當子類需要修改父類方法的實現時,使用方法重寫。例如不同動物的叫聲不同:

複製

// 父類
public class Animal {
    public void shout() {
        System.out.println("動物發出叫聲");
    }
}

// 子類重寫父類方法
public class Dog extends Animal {
    @Override  // 註解:明確標識這是重寫的方法
    public void shout() {
        System.out.println("汪汪汪");
    }
}

public class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵");
    }
}

運行以下代碼會得到不同結果:

複製

public class Test {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Cat();
        a1.shout();  // 輸出:汪汪汪(實際調用的是Dog類的方法)
        a2.shout();  // 輸出:喵喵喵(實際調用的是Cat類的方法)
    }
}

方法重寫規則表

Java21天學習計劃 - 第六天:面向對象基礎之繼承與多態入門_子類_02

核心規則

  1. 方法簽名必須相同:方法名、參數列表完全一致(返回值類型需兼容)
  2. 訪問權限不能降低:如父類是 public,子類不能改為 private
  3. 不能拋出更寬泛的異常:子類拋出的異常範圍不能大於父類
  4. 用 @Override 註解:幫助編譯器檢查重寫是否正確

方法重載(Overload)

在同一個類中,允許存在多個同名方法,但參數列表必須不同(參數類型、個數或順序不同):

複製

public class Calculator {
    // 兩個整數相加
    public int add(int a, int b) {
        return a + b;
    }

    // 三個整數相加(參數個數不同)
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 兩個小數相加(參數類型不同)
    public double add(double a, double b) {
        return a + b;
    }
}

重寫與重載的區別

表格

複製

特性

方法重寫(Override)

方法重載(Overload)

發生位置

子類與父類之間

同一個類中

方法名稱

必須相同

必須相同

參數列表

必須相同

必須不同(類型/個數/順序)

返回值類型

子類 <= 父類(兼容類型)

可以不同

訪問修飾符

子類 >= 父類

可以不同

多態性體現

運行時多態

編譯時多態

super關鍵字:訪問父類成員的橋樑

當子類重寫了父類方法或隱藏了父類屬性時,如何訪問父類的版本?super關鍵字就是專門用於調用父類成員的工具。

super的三種用法

  1. 調用父類構造方法:super(參數),必須放在子類構造方法第一行
  2. 調用父類普通方法:super.方法名(參數)
  3. 訪問父類屬性:super.屬性名

複製

public class Animal {
    private String name;

    // 父類構造方法
    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + "在吃東西");
    }
}

public class Dog extends Animal {
    private int age;

    // 子類構造方法
    public Dog(String name, int age) {
        super(name);  // 調用父類構造方法
        this.age = age;
    }

    @Override
    public void eat() {
        super.eat();  // 調用父類的eat方法
        System.out.println("狗喜歡啃骨頭");
    }
}

super與this的區別

表格

複製

關鍵字

訪問對象

調用構造方法

查找範圍

this

當前對象

this(參數)

當前類 -> 父類

super

父類對象

super(參數)

直接父類

訪問修飾符與繼承:控制成員的可見範圍

在第五天我們學習了四種訪問修飾符,它們在繼承中有着特殊的作用——決定父類成員能否被子類訪問。

訪問修飾符權限表

表格

複製

修飾符

本類

同包

子類

其他包

private

default(缺省)

protected

public

繼承中的訪問規則

  • private成員:子類完全不可見,需通過父類的getter/setter訪問
  • protected成員:不同包子類只能通過繼承關係訪問(不能用父類引用訪問)
  • 構造方法:不能繼承,但子類可通過super調用

複製

// 父類(com.animal包)
package com.animal;
public class Animal {
    private String color;      // 私有成員(子類不可見)
    protected String name;     // 保護成員(子類可見)
    public int age;            // 公共成員(全部可見)

    public String getColor() { // 公共方法訪問私有屬性
        return color;
    }
}

// 子類(com.pet包)
package com.pet;
import com.animal.Animal;

public class Dog extends Animal {
    public void showInfo() {
        // System.out.println(color); // 編譯錯誤:private成員不可見
        System.out.println(name);    // 正確:protected成員可訪問
        System.out.println(age);     // 正確:public成員可訪問
        System.out.println(getColor()); // 正確:通過公共方法訪問私有屬性
    }
}

final關鍵字:不可變的終極修飾符

如果你希望某個類不能被繼承,或者某個方法不能被重寫,final關鍵字可以幫你實現。它就像給代碼加上"不可修改"的封印。

final的四種用法

  1. 修飾類:該類不能被繼承(如 String 類)

複製

public final class MathUtil {
       // 工具類通常設為final,避免被繼承篡改
   }

  1. 修飾方法:該方法不能被重寫

複製

public class Animal {
       public final void breathe() {
           System.out.println("用肺呼吸"); // 核心方法不允許重寫
       }
   }

  1. 修飾變量:變量值不能被修改(常量)

複製

public class Circle {
       public static final double PI = 3.1415926; // 靜態常量
       private final String id; // 實例常量

       public Circle(String id) {
           this.id = id; // 只能在構造方法中賦值
       }
   }

  1. 修飾參數:方法參數在方法內不能被修改

複製

public void printInfo(final String name) {
       // name = "新名字"; // 編譯錯誤:final參數不可修改
       System.out.println(name);
   }

final與繼承的關係

  • final類沒有子類,因此所有方法默認都是final的
  • final方法可以被繼承,但不能被重寫
  • final變量在繼承中可被子類使用,但不能被修改

繼承中的構造方法執行順序

當創建子類對象時,父類構造方法會先於子類構造方法執行——這是因為子類依賴父類的初始化。我們通過一個案例來觀察完整執行流程:

構造方法執行順序示例

複製

public class Animal {
    public Animal() {
        System.out.println("1. Animal無參構造");
    }

    public Animal(String name) {
        System.out.println("1. Animal有參構造:" + name);
    }
}

public class Dog extends Animal {
    public Dog() {
        // super(); // 隱式調用父類無參構造
        System.out.println("2. Dog無參構造");
    }

    public Dog(String name) {
        super(name); // 顯式調用父類有參構造
        System.out.println("2. Dog有參構造:" + name);
    }
}

// 測試類
public class Test {
    public static void main(String[] args) {
        System.out.println("創建Dog對象:");
        Dog dog = new Dog("旺財");
    }
}

執行結果

複製

創建Dog對象:
1. Animal有參構造:旺財
2. Dog有參構造:旺財

繼承中的構造方法執行順序示意圖

Java21天學習計劃 - 第六天:面向對象基礎之繼承與多態入門_子類_03

執行規則

  1. 調用父類構造方法(顯式用 super(參數),隱式調用無參 super())
  2. 初始化子類成員變量
  3. 執行子類構造方法體

⚠️ 注意:如果父類沒有無參構造,子類必須顯式調用父類有參構造,否則編譯錯誤!

實踐案例:動物類繼承體系

現在我們綜合運用今天所學知識,設計一個動物類繼承體系,包含以下功能:

  • 父類 Animal 定義共同屬性和方法
  • 子類 Dog、Cat 重寫父類方法並添加特有功能
  • 使用 super 調用父類構造方法
  • 用 final 定義不可修改的常量

動物類繼承體系代碼實現

複製

// 動物父類
public class Animal {
    protected String name;
    protected String color;
    public static final int LEG_COUNT = 4; // 動物腿數常量(默認4條腿)

    public Animal(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public void shout() {
        System.out.println(name + "發出叫聲");
    }

    public void eat() {
        System.out.println(name + "在進食");
    }
}

// 狗子類
public class Dog extends Animal {
    private String breed; // 品種

    public Dog(String name, String color, String breed) {
        super(name, color); // 調用父類構造方法
        this.breed = breed;
    }

    @Override
    public void shout() {
        System.out.println(name + "汪汪叫");
    }

    public void guardHouse() {
        System.out.println(name + "正在看家");
    }
}

// 貓子類
public class Cat extends Animal {
    private int age; // 年齡

    public Cat(String name, String color, int age) {
        super(name, color); // 調用父類構造方法
        this.age = age;
    }

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

    public void catchMouse() {
        System.out.println(name + "抓老鼠");
    }
}

// 測試類
public class AnimalTest {
    public static void main(String[] args) {
        Animal dog = new Dog("旺財", "黃色", "金毛");
        Animal cat = new Cat("咪咪", "白色", 3);

        dog.shout(); // 多態:調用Dog的shout
        dog.eat();   // 繼承:調用Animal的eat
        ((Dog) dog).guardHouse(); // 向下轉型,調用Dog特有方法

        cat.shout(); // 多態:調用Cat的shout
        ((Cat) cat).catchMouse(); // 向下轉型,調用Cat特有方法

        System.out.println("動物腿數:" + Animal.LEG_COUNT); // 訪問常量
    }
}

動物類繼承關係圖

Java21天學習計劃 - 第六天:面向對象基礎之繼承與多態入門_System_04

運行結果

複製

旺財汪汪叫
旺財在進食
旺財正在看家
咪咪喵喵叫
咪咪抓老鼠
動物腿數:4

總結與明日預告

今天我們學習了面向對象的核心特性——繼承,通過 extends 實現代碼複用,用 super 訪問父類成員,用 final 限制修改,還掌握了方法重寫與重載的區別。這些知識讓我們的代碼從"零散類"升級為"有機體系",為明日學習多態打下基礎。

今日重點

  • 繼承通過 extends 實現,子類複用父類代碼並擴展新功能
  • 方法重寫要求籤名相同,實現運行時多態
  • super用於調用父類構造、方法和屬性
  • 訪問修飾符控制繼承中的成員可見性
  • final可修飾類、方法、變量,實現不可變特性

明日內容:多態的實現原理、抽象類與接口、Object類常用方法。我們將學習如何用多態設計靈活的代碼架構,敬請期待!

課後練習

  1. 設計一個Shape父類,包含計算面積和周長的方法,然後派生出Circle、Rectangle子類實現具體計算
  2. 嘗試用final修飾一個工具類,體會不可繼承的特性
  3. 思考:為什麼Java不支持多重繼承?(提示:菱形繼承問題)