《Java 程序設計》第 7 章 - 繼承與多態

引言

????????在 Java 面向對象編程中,繼承多態是兩大核心特性,它們共同支撐了代碼的復用性、擴展性和靈活性。本章將從繼承的基本實現開始,逐步深入到方法覆蓋、訪問控制、抽象類等概念,最終揭示多態的本質與應用。通過大量可運行的代碼示例和直觀的圖表,幫助你徹底掌握這些重要知識點。

7.1 類的繼承

????????繼承是面向對象編程的三大特性之一,它允許我們基于已有的類創建新類,從而實現代碼復用和擴展。被繼承的類稱為父類(超類),新創建的類稱為子類(派生類)

7.1.1 類繼承的實現

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

class 子類名 extends 父類名 {// 子類新增屬性和方法
}

核心特點

  • 子類擁有父類的非私有屬性和方法(代碼復用)
  • 子類可以新增自己的屬性和方法(功能擴展)
  • Java 只支持單繼承(一個子類只能有一個直接父類)

代碼示例:基礎繼承實現

// 父類:動物
class Animal {// 父類屬性protected String name;// 父類方法public void eat() {System.out.println(name + "正在吃東西");}public void sleep() {System.out.println(name + "正在睡覺");}
}// 子類:狗(繼承自動物)
class Dog extends Animal {// 子類新增屬性private String breed; // 品種// 子類新增方法public void bark() {System.out.println(name + "在汪汪叫");}// 子類setter方法(設置名字和品種)public void setInfo(String name, String breed) {this.name = name; // 直接訪問父類的protected屬性this.breed = breed;}public void showBreed() {System.out.println("品種:" + breed);}
}// 測試類
public class InheritanceDemo {public static void main(String[] args) {// 創建子類對象Dog dog = new Dog();dog.setInfo("旺財", "金毛");// 調用父類繼承的方法dog.eat();   // 輸出:旺財正在吃東西dog.sleep(); // 輸出:旺財正在睡覺// 調用子類新增的方法dog.bark();     // 輸出:旺財在汪汪叫dog.showBreed();// 輸出:品種:金毛}
}

類圖:Animal 與 Dog 的繼承關系

@startuml
class Animal {- String name+ void eat()+ void sleep()
}class Dog {- String breed+ void bark()+ void setInfo(String, String)+ void showBreed()
}Animal <|-- Dog : extends
@enduml

7.1.2 方法覆蓋

????????當子類需要修改父類的方法實現時,可以使用方法覆蓋(Override),也稱為方法重寫。

方法覆蓋的規則

  1. 方法名、參數列表必須與父類完全相同
  2. 返回值類型:父類返回值為T,子類可以是TT的子類(協變返回)
  3. 訪問權限:子類方法權限不能低于父類(如父類protected,子類可protectedpublic
  4. 不能拋出比父類更多的 checked 異常
  5. @Override注解顯式聲明(非必須,但推薦,編譯器會校驗正確性)

代碼示例:方法覆蓋實現

// 父類:形狀
class Shape {// 父類方法:計算面積(默認實現)public double calculateArea() {System.out.println("形狀的面積計算");return 0.0;}
}// 子類:圓形(重寫面積計算方法)
class Circle extends Shape {private double radius; // 半徑public Circle(double radius) {this.radius = radius;}// 重寫父類的面積計算方法@Overridepublic double calculateArea() {System.out.println("圓形的面積計算");return Math.PI * radius * radius; // 圓面積公式:πr2}
}// 子類:矩形(重寫面積計算方法)
class Rectangle extends Shape {private double length; // 長private double width;  // 寬public Rectangle(double length, double width) {this.length = length;this.width = width;}// 重寫父類的面積計算方法@Overridepublic double calculateArea() {System.out.println("矩形的面積計算");return length * width; // 矩形面積公式:長×寬}
}// 測試類
public class OverrideDemo {public static void main(String[] args) {Shape circle = new Circle(5);System.out.println("圓面積:" + circle.calculateArea()); // 輸出:圓形的面積計算 圓面積:78.539...Shape rectangle = new Rectangle(4, 6);System.out.println("矩形面積:" + rectangle.calculateArea()); // 輸出:矩形的面積計算 矩形面積:24.0}
}

7.1.3 super 關鍵字

super關鍵字用于訪問父類的屬性、方法和構造器,主要場景:

  • 調用父類的非私有屬性:super.屬性名
  • 調用父類的非私有方法:super.方法名(參數)
  • 調用父類的構造器:super(參數)(必須放在子類構造器第一行)

代碼示例:super 關鍵字的使用

// 父類:員工
class Employee {protected String name;protected double salary;// 父類構造器public Employee(String name, double salary) {this.name = name;this.salary = salary;}// 父類方法public void showInfo() {System.out.println("姓名:" + name + ",薪資:" + salary);}
}// 子類:經理(繼承自員工)
class Manager extends Employee {private double bonus; // 獎金// 子類構造器public Manager(String name, double salary, double bonus) {super(name, salary); // 調用父類構造器(必須在第一行)this.bonus = bonus;}// 重寫父類方法,并用super調用父類方法@Overridepublic void showInfo() {super.showInfo(); // 調用父類的showInfo()System.out.println("獎金:" + bonus + ",總薪資:" + (salary + bonus));}// 子類方法:使用super訪問父類屬性public void raiseSalary(double percent) {// 父類salary是protected,子類可通過super訪問salary = salary * (1 + percent / 100) + bonus; System.out.println(name + "的薪資已調整為:" + salary);}
}// 測試類
public class SuperDemo {public static void main(String[] args) {Manager manager = new Manager("張三", 8000, 2000);manager.showInfo(); // 輸出:// 姓名:張三,薪資:8000.0// 獎金:2000.0,總薪資:10000.0manager.raiseSalary(10); // 漲薪10%// 輸出:張三的薪資已調整為:9800.0}
}

7.1.4 調用父類的構造方法

????????子類構造器中,默認會隱式調用父類的無參構造器super());如果父類沒有無參構造器,子類必須顯式調用父類的有參構造器(super(參數)),否則編譯報錯。

流程圖:構造器調用順序

代碼示例:父類構造器調用

// 父類:Person
class Person {private String name;private int age;// 父類有參構造器(注意:沒有無參構造器)public Person(String name, int age) {this.name = name;this.age = age;System.out.println("Person構造器被調用:" + name + "," + age + "歲");}
}// 子類:Student(繼承自Person)
class Student extends Person {private String school; // 學校// 子類構造器:必須顯式調用父類有參構造器public Student(String name, int age, String school) {super(name, age); // 顯式調用父類構造器(否則編譯報錯)this.school = school;System.out.println("Student構造器被調用:" + school);}
}// 測試類
public class ConstructorCallDemo {public static void main(String[] args) {// 創建子類對象時,先調用父類構造器,再調用子類構造器Student student = new Student("李四", 18, "北京大學");// 輸出:// Person構造器被調用:李四,18歲// Student構造器被調用:北京大學}
}

7.1 綜合案例:動物繼承體系

需求:設計動物繼承體系,包含父類Animal子類DogCat,展示繼承、方法覆蓋和 super 關鍵字的綜合應用。

類圖

@startuml
class Animal {- String name+ Animal(String name)+ void eat()+ void makeSound()+ void sleep()
}class Dog {+ Dog(String name)+ void makeSound()+ void fetch()
}class Cat {+ Cat(String name)+ void makeSound()+ void climbTree()
}Animal <|-- Dog
Animal <|-- Cat
@enduml

完整代碼

// 父類:動物
class Animal {protected String name; // 名字// 父類構造器public Animal(String name) {this.name = name;System.out.println("Animal構造器:" + name);}// 吃東西(通用實現)public void eat() {System.out.println(name + "在吃東西");}// 發出聲音(父類默認實現)public void makeSound() {System.out.println(name + "發出聲音");}// 睡覺(通用實現)public void sleep() {System.out.println(name + "在睡覺");}
}// 子類:狗
class Dog extends Animal {// 子類構造器public Dog(String name) {super(name); // 調用父類構造器System.out.println("Dog構造器:" + name);}// 重寫:狗的叫聲@Overridepublic void makeSound() {System.out.println(name + "汪汪叫");}// 子類特有方法:撿東西public void fetch() {System.out.println(name + "在撿球");super.eat(); // 調用父類的eat()方法}
}// 子類:貓
class Cat extends Animal {// 子類構造器public Cat(String name) {super(name); // 調用父類構造器System.out.println("Cat構造器:" + name);}// 重寫:貓的叫聲@Overridepublic void makeSound() {System.out.println(name + "喵喵叫");}// 子類特有方法:爬樹public void climbTree() {System.out.println(name + "在爬樹");}
}// 測試類
public class AnimalInheritanceDemo {public static void main(String[] args) {System.out.println("===== 創建Dog對象 =====");Dog dog = new Dog("旺財");dog.eat();      // 繼承父類方法dog.makeSound();// 調用重寫的方法dog.sleep();    // 繼承父類方法dog.fetch();    // 子類特有方法System.out.println("\n===== 創建Cat對象 =====");Cat cat = new Cat("咪咪");cat.eat();      // 繼承父類方法cat.makeSound();// 調用重寫的方法cat.sleep();    // 繼承父類方法cat.climbTree();// 子類特有方法}
}

運行結果

7.2 封裝性與訪問修飾符

????????封裝是將數據和操作數據的方法捆綁在一起,并通過訪問修飾符控制外部訪問權限,實現 "數據隱藏"。

7.2.1 類的訪問權限

Java 中類的訪問權限只有兩種:

  • public:公開類,可被所有包中的類訪問
  • 默認權限(無修飾符):包內可見,僅同一包中的類可訪問

規則

  • 一個 Java 源文件中最多有一個public,且文件名必須與public類名相同
  • 若類為public,其包路徑需與文件夾結構一致

代碼示例:類訪問權限

// 文件:com/example/PublicClass.java(public類)
package com.example;
public class PublicClass {public void publicMethod() {System.out.println("public類的public方法");}
}// 文件:com/example/DefaultClass.java(默認權限類)
package com.example;
class DefaultClass { // 無訪問修飾符,默認權限public void defaultClassMethod() {System.out.println("默認類的public方法");}
}// 文件:com/other/TestClass.java(不同包的測試類)
package com.other;
import com.example.PublicClass;
// import com.example.DefaultClass; // 編譯報錯:DefaultClass是默認權限,不同包不可訪問public class TestClass {public static void main(String[] args) {PublicClass publicObj = new PublicClass();publicObj.publicMethod(); // 正常訪問:public類可跨包訪問// DefaultClass defaultObj = new DefaultClass(); // 編譯報錯:無法訪問默認權限類}
}

7.2.2 類成員的訪問權限

類成員(屬性和方法)有 4 種訪問權限,權限從大到小為:

修飾符本類同包類不同包子類其他類
public????????
protected???????
默認??????
private?????

最佳實踐

  • 屬性通常用private修飾,通過publicgetter/setter方法訪問
  • 方法根據需要設置權限,對外暴露的接口用public,內部工具方法用private
  • 父子類共享的方法 / 屬性用protected

代碼示例:成員訪問權限

// 父類:com/example/Parent.java
package com.example;
public class Parent {public String publicField = "public屬性";protected String protectedField = "protected屬性";String defaultField = "default屬性"; // 默認權限private String privateField = "private屬性";public void publicMethod() {System.out.println("public方法:" + privateField); // 本類可訪問private}protected void protectedMethod() {System.out.println("protected方法");}void defaultMethod() {System.out.println("default方法");}private void privateMethod() {System.out.println("private方法");}
}// 同包子類:com/example/ChildSamePackage.java
package com.example;
public class ChildSamePackage extends Parent {public void accessParent() {System.out.println(publicField); // ?? publicSystem.out.println(protectedField); // ?? protectedSystem.out.println(defaultField); // ?? 同包默認權限// System.out.println(privateField); // ? 不可訪問privatepublicMethod(); // ??protectedMethod(); // ??defaultMethod(); // ?? 同包// privateMethod(); // ?}
}// 不同包子類:com/other/ChildDifferentPackage.java
package com.other;
import com.example.Parent;
public class ChildDifferentPackage extends Parent {public void accessParent() {System.out.println(publicField); // ?? publicSystem.out.println(protectedField); // ?? protected(子類)// System.out.println(defaultField); // ? 不同包默認權限不可訪問// System.out.println(privateField); // ?publicMethod(); // ??protectedMethod(); // ?? 子類可訪問// defaultMethod(); // ? 不同包默認方法不可訪問// privateMethod(); // ?}
}// 不同包非子類:com/other/OtherClass.java
package com.other;
import com.example.Parent;
public class OtherClass {public void accessParent() {Parent parent = new Parent();System.out.println(parent.publicField); // ?? public// System.out.println(parent.protectedField); // ? 非子類不可訪問protected// System.out.println(parent.defaultField); // ? 不同包默認不可訪問// System.out.println(parent.privateField); // ?parent.publicMethod(); // ??// parent.protectedMethod(); // ? 非子類不可訪問// parent.defaultMethod(); // ?// parent.privateMethod(); // ?}
}

7.2 綜合案例:封裝與訪問控制

需求:設計一個User類,通過訪問修飾符實現封裝,提供安全的屬性訪問方式。

完整代碼

package com.example.encapsulation;// 用戶類(封裝示例)
public class User {// 屬性私有化(private)private String username; // 用戶名private String password; // 密碼private int age;         // 年齡// 無參構造器public User() {}// 有參構造器public User(String username, String password, int age) {this.username = username;this.password = password;this.age = age;}// 用戶名的getter(public,對外提供讀取權限)public String getUsername() {return username;}// 密碼的getter(僅返回脫敏后的密碼)public String getPasswordMasked() {if (password == null || password.length() <= 2) {return "***";}return password.substring(0, 2) + "***"; // 前2位顯示,其余脫敏}// 密碼的setter(提供修改權限,帶簡單驗證)public void setPassword(String password) {if (password == null || password.length() < 6) {throw new IllegalArgumentException("密碼長度不能少于6位");}this.password = password;}// 年齡的getterpublic int getAge() {return age;}// 年齡的setter(帶驗證邏輯)public void setAge(int age) {if (age < 0 || age > 150) {throw new IllegalArgumentException("年齡必須在0-150之間");}this.age = age;}// 公開方法:用戶登錄public boolean login(String inputPassword) {return password.equals(inputPassword); // 內部可訪問private屬性}
}// 測試類
public class EncapsulationDemo {public static void main(String[] args) {User user = new User("zhangsan", "123456", 25);// 訪問用戶名(通過getter)System.out.println("用戶名:" + user.getUsername()); // 輸出:用戶名:zhangsan// 訪問脫敏密碼System.out.println("密碼(脫敏):" + user.getPasswordMasked()); // 輸出:密碼(脫敏):12***// 測試年齡設置user.setAge(30);System.out.println("年齡:" + user.getAge()); // 輸出:年齡:30// 測試密碼設置(合法)user.setPassword("newpass123");System.out.println("修改密碼后(脫敏):" + user.getPasswordMasked()); // 輸出:ne***// 測試登錄boolean loginSuccess = user.login("newpass123");System.out.println("登錄成功?" + loginSuccess); // 輸出:true// 測試非法年齡(會拋出異常)try {user.setAge(200);} catch (IllegalArgumentException e) {System.out.println("年齡設置錯誤:" + e.getMessage()); // 輸出:年齡必須在0-150之間}}
}

7.3 防止類擴展和方法覆蓋

final關鍵字用于限制類、方法或變量的修改,實現 "不可變" 特性。

7.3.1 final 修飾類

final修飾的類不能被繼承(最終類),確保類的功能不被修改。

典型應用

  • JDK 中的StringInteger等類都是final
  • 工具類通常設計為final(如java.util.Math

代碼示例:final 類

// final類:不能被繼承
final class FinalClass {public void show() {System.out.println("這是final類的方法");}
}// 嘗試繼承final類(編譯報錯)
// class SubClass extends FinalClass { // 錯誤:無法從最終類FinalClass繼承
//     @Override
//     public void show() {
//         System.out.println("嘗試覆蓋final類的方法");
//     }
// }// 測試類
public class FinalClassDemo {public static void main(String[] args) {FinalClass obj = new FinalClass();obj.show(); // 輸出:這是final類的方法}
}

7.3.2 final 修飾方法

final修飾的方法不能被子類覆蓋,但類可以被繼承。

應用場景

  • 確保核心方法的實現不被修改
  • 提升性能(JVM 可能對 final 方法進行優化)

代碼示例:final 方法

// 父類:包含final方法
class ParentWithFinalMethod {// final方法:不能被覆蓋public final void finalMethod() {System.out.println("這是final方法,不能被覆蓋");}// 普通方法:可以被覆蓋public void normalMethod() {System.out.println("這是普通方法,可以被覆蓋");}
}// 子類
class ChildWithFinalMethod extends ParentWithFinalMethod {// 嘗試覆蓋final方法(編譯報錯)// @Override// public void finalMethod() { // 錯誤:final方法不能被覆蓋//     System.out.println("嘗試覆蓋final方法");// }// 覆蓋普通方法(合法)@Overridepublic void normalMethod() {System.out.println("子類覆蓋了普通方法");}
}// 測試類
public class FinalMethodDemo {public static void main(String[] args) {ChildWithFinalMethod child = new ChildWithFinalMethod();child.finalMethod();   // 輸出:這是final方法,不能被覆蓋child.normalMethod();  // 輸出:子類覆蓋了普通方法}
}

7.3.3 final 修飾變量

final修飾的變量只能被賦值一次(常量),賦值后不可修改。

特性

  • 局部變量:聲明時或構造器中賦值
  • 成員變量:聲明時、構造塊或構造器中賦值(必須保證創建對象時已初始化)
  • 引用類型變量:引用地址不可變,但對象內容可修改

代碼示例:final 變量

public class FinalVariableDemo {// 成員常量:聲明時賦值public static final double PI = 3.14159; // 靜態常量(通常全大寫)private final String name; // 實例常量// 構造塊中初始化final變量(可選){// name = "默認名稱"; // 若此處賦值,構造器中不可再賦值}// 構造器中初始化final變量public FinalVariableDemo(String name) {this.name = name; // 必須賦值,否則編譯報錯}public void showFinalVars() {// 局部final變量final int MAX_COUNT;MAX_COUNT = 100; // 第一次賦值(合法)// MAX_COUNT = 200; // 錯誤:final變量不能重復賦值System.out.println("PI:" + PI);System.out.println("name:" + name);System.out.println("MAX_COUNT:" + MAX_COUNT);}public void modifyFinalObject() {// final引用類型變量final StringBuilder sb = new StringBuilder("final引用");sb.append(",但內容可修改"); // 合法:對象內容可改System.out.println(sb.toString()); // 輸出:final引用,但內容可修改// sb = new StringBuilder("新對象"); // 錯誤:引用地址不可改}public static void main(String[] args) {FinalVariableDemo demo = new FinalVariableDemo("測試");demo.showFinalVars();demo.modifyFinalObject();}
}

7.4 抽象類

????????抽象類(abstract?class)是包含抽象方法的類,它不能被實例化只能作為父類被繼承。抽象類用于定義通用模板,強制子類實現特定方法。

核心特性

  • abstract關鍵字修飾
  • 可包含抽象方法(無實現的方法)和具體方法(有實現)
  • 子類必須實現所有抽象方法,否則子類也必須是抽象類
  • 不能用final修飾(抽象類必須能被繼承)

類圖:抽象類與子類關系

@startuml
abstract class Shape {+ abstract double calculateArea()+ abstract double calculatePerimeter()+ void printInfo()
}class Circle {- double radius+ Circle(double radius)+ double calculateArea()+ double calculatePerimeter()
}class Rectangle {- double length- double width+ Rectangle(double length, double width)+ double calculateArea()+ double calculatePerimeter()
}Shape <|-- Circle
Shape <|-- Rectangle
@enduml

代碼示例:抽象類應用

// 抽象類:形狀(定義通用模板)
abstract class Shape {// 抽象方法:計算面積(無實現,由子類實現)public abstract double calculateArea();// 抽象方法:計算周長public abstract double calculatePerimeter();// 具體方法:打印形狀信息(已有實現)public void printInfo() {System.out.println("面積:" + calculateArea() + ",周長:" + calculatePerimeter());}
}// 子類:圓形(必須實現所有抽象方法)
class Circle extends Shape {private double radius; // 半徑public Circle(double radius) {this.radius = radius;}// 實現抽象方法:計算面積@Overridepublic double calculateArea() {return Math.PI * radius * radius;}// 實現抽象方法:計算周長@Overridepublic double calculatePerimeter() {return 2 * Math.PI * radius;}
}// 子類:矩形
class Rectangle extends Shape {private double length; // 長private double width;  // 寬public Rectangle(double length, double width) {this.length = length;this.width = width;}@Overridepublic double calculateArea() {return length * width;}@Overridepublic double calculatePerimeter() {return 2 * (length + width);}
}// 測試類
public class AbstractClassDemo {public static void main(String[] args) {// 抽象類不能實例化// Shape shape = new Shape(); // 錯誤:Shape是抽象的,無法實例化// 創建子類對象,用父類引用接收Shape circle = new Circle(5);System.out.println("圓形信息:");circle.printInfo(); // 調用抽象類的具體方法,實際執行子類實現Shape rectangle = new Rectangle(4, 6);System.out.println("\n矩形信息:");rectangle.printInfo();}
}

運行結果

7.5 對象轉換與多態

????????對象轉換和多態是實現靈活編程的關鍵,它們允許我們用統一的方式處理不同類型的對象

7.5.1 對象轉換

對象轉換分為向上轉型向下轉型

  • 向上轉型(自動轉換):子類對象 → 父類引用(Parent p = new Child();
  • 向下轉型(強制轉換):父類引用 → 子類對象(Child c = (Child)p;,需確保安全性)

流程圖:對象轉換流程

代碼示例:對象轉換

// 父類:Animal
class Animal {public void eat() {System.out.println("動物吃東西");}
}// 子類:Dog
class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃骨頭");}// 子類特有方法public void bark() {System.out.println("狗汪汪叫");}
}// 子類:Cat
class Cat extends Animal {@Overridepublic void eat() {System.out.println("貓吃魚");}// 子類特有方法public void meow() {System.out.println("貓喵喵叫");}
}// 測試類
public class ObjectCastDemo {public static void main(String[] args) {// 1. 向上轉型(自動)Animal animal = new Dog(); // animal引用指向Dog對象animal.eat(); // 輸出:狗吃骨頭(多態)// 2. 嘗試直接調用子類特有方法(編譯報錯)// animal.bark(); // 錯誤:Animal類沒有bark()方法// 3. 向下轉型(強制)if (animal instanceof Dog) { // 先判斷類型(安全)Dog dog = (Dog) animal; // 強制轉換dog.bark(); // 輸出:狗汪汪叫(調用子類特有方法)}// 4. 錯誤的向下轉型(運行時異常)Animal animal2 = new Cat();// Dog dog2 = (Dog) animal2; // 編譯通過,但運行時拋出ClassCastException}
}

7.5.2 instanceof 運算符

instanceof用于判斷對象的實際類型返回boolean,語法:對象 instanceof 類型

用途

  • 向下轉型前檢查類型,避免ClassCastException
  • 判斷對象是否屬于某個類或其子類

代碼示例:instanceof 使用

// 復用上面的Animal、Dog、Cat類public class InstanceOfDemo {public static void main(String[] args) {Animal animal = new Dog();// 判斷animal是否是Dog類型System.out.println(animal instanceof Dog); // true// 判斷animal是否是Animal類型(父類)System.out.println(animal instanceof Animal); // true// 判斷animal是否是Cat類型System.out.println(animal instanceof Cat); // false// 空對象的instanceof判斷Animal nullAnimal = null;System.out.println(nullAnimal instanceof Animal); // false(空對象返回false)// 安全的向下轉型if (animal instanceof Dog) {Dog dog = (Dog) animal;dog.bark(); // 安全調用}}
}

7.5.3 多態與動態綁定

????????多態(Polymorphism)指同一操作作用于不同對象,產生不同結果。表現為:父類引用指向子類對象,調用方法時實際執行子類的實現。

????????動態綁定(Dynamic Binding):程序運行時,JVM 根據對象的實際類型確定調用哪個方法的過程(而非引用類型)。

多態的條件

  1. 存在繼承關系
  2. 子類覆蓋父類方法
  3. 父類引用指向子類對象

代碼示例:多態與動態綁定

// 父類:Shape
class Shape {public void draw() {System.out.println("繪制形狀");}
}// 子類:Circle
class Circle extends Shape {@Overridepublic void draw() {System.out.println("繪制圓形");}
}// 子類:Rectangle
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("繪制矩形");}
}// 工具類:繪圖工具(多態應用)
class DrawingTool {// 接收父類引用,實現通用繪圖方法public static void drawShape(Shape shape) {shape.draw(); // 動態綁定:調用實際類型的draw()}
}// 測試類
public class PolymorphismDemo {public static void main(String[] args) {// 父類引用指向不同子類對象Shape circle = new Circle();Shape rectangle = new Rectangle();// 多態:同一方法調用,不同結果DrawingTool.drawShape(circle);    // 輸出:繪制圓形DrawingTool.drawShape(rectangle); // 輸出:繪制矩形}
}

運行結果

7.5 綜合案例:多態計算器

需求:設計一個計算器,支持整數、小數和字符串拼接的加法運算,用多態實現統一調用接口。

完整代碼

// 抽象父類:計算器操作
abstract class Calculator {// 抽象方法:計算(子類實現不同類型的計算)public abstract Object calculate(Object a, Object b);
}// 子類:整數計算器
class IntegerCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 類型檢查if (!(a instanceof Integer) || !(b instanceof Integer)) {throw new IllegalArgumentException("整數計算器只支持Integer類型");}// 強轉并計算int num1 = (Integer) a;int num2 = (Integer) b;return num1 + num2;}
}// 子類:小數計算器
class DoubleCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 支持Integer和Double混合計算double num1 = (a instanceof Integer) ? (Integer) a : (Double) a;double num2 = (b instanceof Integer) ? (Integer) b : (Double) b;return num1 + num2;}
}// 子類:字符串計算器(拼接)
class StringCalculator extends Calculator {@Overridepublic Object calculate(Object a, Object b) {// 任何類型都轉為字符串拼接return a.toString() + b.toString();}
}// 測試類
public class PolymorphismCalculatorDemo {public static void main(String[] args) {// 創建不同計算器(多態數組)Calculator[] calculators = {new IntegerCalculator(),new DoubleCalculator(),new StringCalculator()};// 測試整數計算System.out.println("整數計算:10 + 20 = " + calculators[0].calculate(10, 20));// 測試小數計算System.out.println("小數計算:3.5 + 4.8 = " + calculators[1].calculate(3.5, 4.8));System.out.println("混和計算:5 + 3.2 = " + calculators[1].calculate(5, 3.2));// 測試字符串拼接System.out.println("字符串拼接:\"Hello\" + \"World\" = " + calculators[2].calculate("Hello", "World"));System.out.println("對象拼接:123 + true = " + calculators[2].calculate(123, true));}
}

運行結果

7.6 小結

本章重點講解了 Java 的繼承與多態特性,核心知識點包括:

  1. 繼承:通過extends實現代碼復用,子類繼承父類的屬性和方法,可通過super訪問父類資源
  2. 方法覆蓋:子類重寫父類方法,需遵循方法簽名、權限等規則,用@Override注解標識
  3. 封裝與訪問修飾符:通過public/protected/default/private控制訪問權限,實現數據安全
  4. final 關鍵字:用于修飾類(不可繼承)、方法(不可覆蓋)、變量(常量)
  5. 抽象類:含抽象方法的類,強制子類實現特定方法,作為通用模板使用
  6. 對象轉換:向上轉型(自動)和向下轉型(強制),instanceof用于類型檢查
  7. 多態與動態綁定:父類引用指向子類對象,運行時調用實際類型的方法,實現靈活編程

編程練習

練習 1:繼承與方法覆蓋

????????需求:設計Person類作為父類,包含nameage屬性及introduce()方法;Student類繼承Person,新增studentId屬性,并重寫introduce()方法;Teacher類繼承Person,新增subject屬性,并重寫introduce()方法。

參考答案

// Person類
class Person {protected String name;protected int age;public Person(String name, int age) {this.name = name;this.age = age;}public void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "歲");}
}// Student類
class Student extends Person {private String studentId;public Student(String name, int age, String studentId) {super(name, age);this.studentId = studentId;}@Overridepublic void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "歲,學號是" + studentId);}
}// Teacher類
class Teacher extends Person {private String subject;public Teacher(String name, int age, String subject) {super(name, age);this.subject = subject;}@Overridepublic void introduce() {System.out.println("大家好,我叫" + name + ",今年" + age + "歲,教" + subject);}
}// 測試類
public class PersonDemo {public static void main(String[] args) {Person student = new Student("小明", 18, "2023001");Person teacher = new Teacher("李老師", 35, "數學");student.introduce(); // 輸出:大家好,我叫小明,今年18歲,學號是2023001teacher.introduce(); // 輸出:大家好,我叫李老師,今年35歲,教數學}
}

練習 2:多態應用

????????需求:設計抽象類Vehicle,包含抽象方法run()CarBicycleTrain類繼承Vehicle并實現run();創建工具類TrafficTool,用多態方法start(Vehicle vehicle)調用不同交通工具的運行方法。

參考答案

// 抽象類:交通工具
abstract class Vehicle {public abstract void run();
}// 汽車
class Car extends Vehicle {@Overridepublic void run() {System.out.println("汽車在公路上行駛");}
}// 自行車
class Bicycle extends Vehicle {@Overridepublic void run() {System.out.println("自行車在自行車道騎行");}
}// 火車
class Train extends Vehicle {@Overridepublic void run() {System.out.println("火車在鐵軌上行駛");}
}// 工具類
class TrafficTool {public static void start(Vehicle vehicle) {System.out.print("交通工具啟動:");vehicle.run(); // 多態調用}
}// 測試類
public class VehicleDemo {public static void main(String[] args) {Vehicle car = new Car();Vehicle bicycle = new Bicycle();Vehicle train = new Train();TrafficTool.start(car);     // 輸出:交通工具啟動:汽車在公路上行駛TrafficTool.start(bicycle); // 輸出:交通工具啟動:自行車在自行車道騎行TrafficTool.start(train);   // 輸出:交通工具啟動:火車在鐵軌上行駛}
}

練習 3:抽象類與 final 綜合應用

????????需求:設計抽象類BankAccount(銀行賬戶),包含抽象方法calculateInterest()(計算利息);SavingsAccount(儲蓄賬戶)和CurrentAccount(活期賬戶)繼承并實現利息計算;用final修飾BankAccountaccountNumber屬性(賬號不可修改)。

參考答案

// 抽象類:銀行賬戶
abstract class BankAccount {// 賬號:final修飾,不可修改private final String accountNumber;protected double balance; // 余額public BankAccount(String accountNumber, double balance) {this.accountNumber = accountNumber;this.balance = balance;}// 抽象方法:計算利息public abstract double calculateInterest();// 獲取賬號(只提供getter,無setter)public String getAccountNumber() {return accountNumber;}// 存款public void deposit(double amount) {if (amount > 0) {balance += amount;System.out.println("存款" + amount + "元,當前余額:" + balance);}}// 顯示賬戶信息public void showAccountInfo() {System.out.println("賬號:" + accountNumber + ",余額:" + balance + "元,利息:" + calculateInterest() + "元");}
}// 儲蓄賬戶(利息3%)
class SavingsAccount extends BankAccount {public SavingsAccount(String accountNumber, double balance) {super(accountNumber, balance);}@Overridepublic double calculateInterest() {return balance * 0.03; // 年利率3%}
}// 活期賬戶(利息0.3%)
class CurrentAccount extends BankAccount {public CurrentAccount(String accountNumber, double balance) {super(accountNumber, balance);}@Overridepublic double calculateInterest() {return balance * 0.003; // 年利率0.3%}
}// 測試類
public class BankAccountDemo {public static void main(String[] args) {BankAccount savings = new SavingsAccount("SA2023001", 10000);BankAccount current = new CurrentAccount("CA2023001", 5000);savings.deposit(2000);current.deposit(1000);savings.showAccountInfo(); // 輸出:賬號:SA2023001,余額:12000.0元,利息:360.0元current.showAccountInfo(); // 輸出:賬號:CA2023001,余額:6000.0元,利息:18.0元}
}

????????希望通過本章內容,你能徹底掌握 Java 繼承與多態的核心概念和實踐技巧。如果有任何問題或代碼運行問題,歡迎在評論區交流!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90836.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90836.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90836.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ksql連接數據庫免輸入密碼交互

ksql連接數據庫免輸入密碼交互1. 使用 .pgpass 文件&#xff08;推薦&#xff09;2. 使用環境變量3. 使用連接字符串4. 修改 ksql 的別名&#xff08;簡化命令&#xff09;5. 注意事項6. 密碼含特殊字符轉義在 Kingbase 中使用 ksql 連接數據庫時&#xff0c;如果希望避免每次手…

UniApp H5 適配 PC 端新姿勢:打造跨設備一致體驗

在移動互聯網蓬勃發展的今天&#xff0c;很多開發者會選擇使用 UniApp 來開發多端應用&#xff0c;尤其是 H5 端&#xff0c;因其無需下載安裝即可訪問的特性&#xff0c;深受用戶喜愛。但 UniApp H5 應用在 PC 端直接打開時&#xff0c;往往會因為屏幕尺寸的巨大差異&#xff…

【MySQL#4】函數 復合查詢 內外連接

&#x1f4c3;個人主頁&#xff1a;island1314 ?? 歡迎關注&#xff1a;&#x1f44d;點贊 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f49e; &#x1f49e; &#x1f49e; 生活總是不會一帆風順&#xff0c;前進的道路也不會永遠一馬平川&#xff0c;如何面…

C++常見面試題之一

一、語言基礎與內存管理const與constexpr的區別&#xff1f;應用場景&#xff1f; const&#xff1a;運行時常量&#xff0c;修飾變量/函數不可修改。constexpr&#xff1a;編譯期常量&#xff08;C11&#xff09;&#xff0c;用于優化計算&#xff08;如數組大小&#xff09;。…

Golang 語言中的指針介紹

介紹&#xff1a;指針式一種數據類型&#xff0c;用來存儲值的內存地址&#xff0c;為了便于理解&#xff0c;我們也可以把指針未內存地址&#xff0c;指針類型只占用內存4個或 8 個字節&#xff0c;在Golang 語言中&#xff0c;類型名稱加 * 表示改類型的指針類型。指針類型變…

Flink 狀態管理設計詳解:StateBackend、State、RocksDB和Namespace

為什么需要 StateBackend&#xff1f;—— 職責分離原則我們可以用一個銀行的例子來類比&#xff1a;State (如 ValueState, ListState) 就像是你的銀行卡。AbstractKeyedStateBackend 就像是銀行的整個后臺系統&#xff08;包括總服務器、數據庫、風控系統、會計系統等&#x…

櫥柜鉸鏈的革命:炬森精密如何以創新科技重塑家居體驗

在現代化家居設計中&#xff0c;櫥柜不僅是存儲空間的核心&#xff0c;更是生活品質的象征。而作為櫥柜的“關節”&#xff0c;櫥柜鉸鏈的性能直接影響著日常使用的便捷性、安全性和耐久性。然而&#xff0c;許多消費者在櫥柜使用中常遭遇噪音干擾、頻繁松動或早期損壞等痛點&a…

醫療系統國產化實錄:SQL Server國產替代,乙方保命指南

醫療行業的SQL Server替代&#xff0c;和普通業務系統完全不是一個量級——醫囑邏輯嵌套幾十層存儲過程、收費結算小數點錯位能引發醫患糾紛、電子病歷查詢慢一秒醫生直接拍桌子。作為被按在手術臺上的乙方PM&#xff0c;實測四款主流國產庫后&#xff0c;掏心窩子說句實話&…

WAIC 2025 盛大啟幕,深思考邀您解鎖端側 AI 新可能?!

2025 世界人工智能大會今日正式啟幕&#xff0c;深思考人工智能&#xff08;iDeepWise&#xff09;攜端側多模態大模型技術成果登陸展會&#xff0c;與行業伙伴共探端側 AI 發展新機遇。作為專注于類端側多模態大模型領域的創新力量&#xff0c;深思考此次重點展示了 iDeepWise…

MySQL相關概念和易錯知識點(2)(表結構的操作、數據類型、約束)

目錄1.表結構的操作&#xff08;1&#xff09;增加表&#xff08;2&#xff09;查看庫中所有的表&#xff08;3&#xff09;查看表每個列的約束&#xff08;4&#xff09;刪除整張表&#xff08;5&#xff09;刪除某個具體的列&#xff08;6&#xff09;增加某個具體的列&#…

1. Qt多線程開發

目錄方法1.繼承QThread使用案例總結方法2.將qobject對象moveToThread&#xff08;官方推薦&#xff09;使用案例總結方法3.QRunnable QThreadPool使用案例總結方法4.快速線程QtConcurrentQFutureWatcher使用案例總結代碼下載方法1.繼承QThread 需要實現QThread的抽象函數run …

ARM入門學習方法分享

首先認識什么是ARM?ARM公司簡介ARM是Advanced RISC Machines的縮寫&#xff0c;它是一家微處理器行業的知名企業&#xff0c;該企業設計了大量高性能、廉價、耗能低的RISC &#xff08;精簡指令集&#xff09;處理器。 1985年第一個ARM原型在英國劍橋誕生。公司的特點是只設計…

基于springboot的在線數碼商城/在線電子產品商品銷售系統的設計與實現

用戶&#xff1a;數碼產品&#xff0c;限時秒殺&#xff0c;種草分享&#xff0c;新品資訊&#xff0c;留言板&#xff0c;訂單管理&#xff0c;在線客服&#xff0c;購物車&#xff0c;個人中心管理員&#xff1a;個人中心&#xff0c;用戶管理&#xff0c;數碼分類管理&#…

Zookeeper學習專欄(十):核心流程剖析之服務啟動、請求處理與選舉協議

文章目錄前言一、服務端啟動流程1.1 啟動入口類&#xff1a;QuorumPeerMain1.2 集群模式啟動核心&#xff1a;runFromConfig1.3 QuorumPeer線程核心邏輯&#xff1a;run()1.4 關鍵子流程&#xff1a;數據恢復1.5 關鍵設計要點二、請求處理鏈&#xff08;責任鏈模式&#xff09;…

網絡基礎19--OSPF路由業務多區域

一、OSPF多區域必要性單區域問題&#xff1a;LSDB龐大 → 內存占用高&#xff0c;SPF計算開銷大LSA洪泛范圍廣 → 拓撲變化影響全域無法路由匯總 → 路由表膨脹&#xff0c;查找效率低2. 多區域優勢&#xff1a;1. 劃分區域&#xff1a;獨立LSDB&#xff0c;縮小數據庫規模2. 限…

MFC擴展庫BCGControlBar Pro v36.2新版亮點:圖形管理器等全新升級

BCGControlBar庫擁有500多個經過全面設計、測試和充分記錄的MFC擴展類。 我們的組件可以輕松地集成到您的應用程序中&#xff0c;并為您節省數百個開發和調試時間。 BCGControlBar專業版 v36.2已全新發布了&#xff0c;在這個版本中添加了一個新的擴展器控件、改進了網格和報表…

QT開發---網絡編程上

Qt Network 模塊Qt Network 模塊提供了豐富的類用于實現各種網絡通信功能&#xff0c;涵蓋 TCP、UDP、HTTP、FTP 等多種協議。 Qt 網絡類均為異步操作&#xff0c;通過信號槽處理結果&#xff0c;避免阻塞 UI 線程。在使用QT進行網絡編程之前&#xff0c;就必須在 CMakeLists.t…

[spring6: Mvc-函數式編程]-源碼解析

接口 ServerRequest public interface ServerRequest {HttpMethod method();URI uri();UriBuilder uriBuilder();default String path() {return requestPath().pathWithinApplication().value();}default RequestPath requestPath() {return ServletRequestPathUtils.getPar…

Linux DNS 服務器正反向解析

一、環境說明與準備工作 1.基礎信息 本次實驗用兩臺 Linux 主機&#xff0c;分別作為 DNS 服務端和客戶端&#xff0c;具體信息如下&#xff1a;服務端IP客戶端IP網址192.168.120.130192.168.120.128www.zy.com2.準備工作 關閉安全軟件&#xff1a;服務端和客戶端都要關閉防火墻…

歷史數據分析——中證旅游

中證旅游板塊走勢從月線級別來看2015年5月到2024年9月&#xff0c;月線上走出了一個震蕩中樞的月線級別下跌段&#xff1b;目前月線級別底部放巨量&#xff0c;總體還在底部震蕩&#xff0c;后續上漲的概率較大。從周線級別來看從2022年12月到2024年9月整體是下跌走勢&#xff…