JAVA SE(9)——多態

1.多態的概念&作用

多態(Polymorphism)是面向對象編程的三大基本特性之一(封裝和繼承已經講過了),它允許不同類的對象對同一消息做出不同的響應。具體來說,多態允許基類/父類的引用指向派生類/子類的對象(向上轉型),并通過該引用調用子類中重寫的方法,從而實現不同的行為

2.實現多態的條件

在Java中,要實現多態必須滿足以下條件,缺一不可:

  • 1.在繼承體系下
  • 2.父類引用指向子類對象(向上轉型)
  • 3.子類必須對父類中的可重寫方法進行重寫
  • 4.通過父類引用調用子類中重寫的方法

在實現多態之前,我們必須先搞清楚向上轉型和重寫是什么

3.向上轉型

3.1 概念

向上轉型是面向對象編程中的一種類型轉換機制,它允許將子類的對象引用轉換為父類的引用。這種類型轉換是安全的,隱式的

3.2 語法格式

父類類型 變量名 = new 子類對象

public class Animal {public int age;public String name;
}
public class Dog extends Animal {public String color;
}
public class Test {public static void main(String[] args) {Animal dog = new Dog();}
}

在上述代碼中,Dog類繼承自Animal類,在實例化Dog對象的時候,創建了一個父類類型的引用(變量)來指向該對象,這就是向上轉型,下面我畫個圖來表示父類引用和子類對象在內存中的關系圖:
在這里插入圖片描述
實現向上轉型有三種方式:
(1)直接賦值:

Animal dog = new Dog();

(2)方法傳參:

public class Test {public static void func(Animal a) {a.eat();}public static void main(String[] args) {Dog dog = new Dog();func(dog);}
}

(3)方法返回值:

public class Test {public static Animal func(String value) {if (value.equals("cat")) {return new Cat();}else if (value.equals("dog")) {return new Dog();}else {return null;}}public static void main(String[] args) {Animal animal = func("cat");}
}

注意:當發生向上轉型后,父類引用無法訪問子類中原有的屬性和方法,只能訪問從父類中繼承而來的屬性和方法,也就是說父類引用的訪問范圍如下:
在這里插入圖片描述
那么,在已經發生向上轉型后,如何才能訪問子類原有的屬性和方法呢?
有兩個辦法:

  • 1.向下轉型
  • 2.使用多態性

4.向下轉型

4.1 如何使用向下轉型

通過顯式地將父類引用轉換為子類引用,可以訪問子類原有的屬性和訪問

public class Animal {public int age;public String name;
}
public class Dog extends Animal {public String color;
}
public class Test {public static void main(String[] args) {//向上轉型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下轉型Dog dog1 = (Dog) dog;System.out.println(dog1.color = "白色");}
}

允許結果:
10
Dog
白色
在這里插入圖片描述

4.2 向下轉型存在的風險

注意:
向下轉型在**編譯**時是允許的,但如果沒有正確地檢查對象的實際類型,運行時可能會拋出ClassCastException異常。這是因為父類引用可能實際上引用的是父類本身或其他子類的對象,而不是目標子類的對象

public class Animal {public int age;public String name;
}
//Dog類繼承Animal
public class Dog extends Animal {public String color;
}
//Cat類繼承Animal
public class Cat extends Animal {public int weight;
}
public class Test {public static void main(String[] args) {//向上轉型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下轉型Dog dog1 = (Dog) dog;System.out.println(dog1.color = "白色");//錯誤的向下轉型Cat cat = (Cat) dog;cat.weight = 10;}
}

上述代碼中,Dog和Cat都繼承自Animal,但是在發生向下轉型的時候,使用Cat類型的引用指向Dog對象。這在編譯時是不會報錯的,因為Cat和Dog同屬于Animal的子類,但是當程序運行之后就會拋出ClassCastException異常

因為向下轉型本質上是強制類型轉換,將Animal dog引用(指向Dog對象的引用)強轉為Dog類型是允許的,因為Dog類型的引用指向Dog對象很合理;但是,如果將Animal dog引用(指向Dog對象的引用)強轉為Cat類型,這是不允許的,因為Cat類型的引用無法指向Dog對象。這就相當于無法將boolean類型強轉為int類型

4.3 instanceof運算符

如果用戶錯誤地使用向下轉型,在編譯階段是不容易被察覺出來的,只有運行階段才會報錯。如果程序運行起來之后才發現錯誤,可能已經帶來了損失,所以為了規避這一情況,Java引入了instanceof運算符來幫助用戶檢測錯誤

public class Animal {public int age;public String name;
}
//Dog類繼承Animal
public class Dog extends Animal {public String color;
}
//Cat類繼承Animal
public class Cat extends Animal {public int weight;
}
public class Test {public static void main(String[] args) {//向上轉型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下轉型if (dog instanceof Dog) {//dog instanceof Dog 為true//說明該向下轉型是安全的Dog dog1 = (Dog) dog;}if (dog instanceof Cat) {//dog instanceof Cat 為false//說明該向下轉型是不安全的,不執行if語句中的代碼Cat cat = (Cat) dog;}}
}

5.重寫

5.1 概念

**重寫(Override)**是面向對象編程中的一個重要概念,它允許子類提供一個與父類中已定義的方法具有相同名稱、參數列表和返回類型的方法。重寫使得子類能夠改變或擴展父類方法的實現

5.2 語法格式

public class Animal {public int age;public String name;//父類的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重寫父類的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}

重寫的規則:

  • 1.子類重寫父類的方法時,必須和父類的方法名、參數列表保持一致
  • 2.返回值類型可以不一樣,但必須具有父子關系。上述代碼中,子類重寫的方法的返回值類型可以是被重寫方法返回值類型的子類
  • 3.子類中重寫方法的訪問權限必須大于等于父類中被重寫的方法
  • 4.父類中被static、final、private修飾的方法以及構造方法不能被重寫
  • 5.重寫方法是可以借助@Override注解。雖然這個注解不影響方法的實現邏輯,但是可以幫助我們進行合法性檢查。比如:如果不小心將方法名寫錯了(寫成了ate),注解就會幫我們報錯
  • 6.重寫只針對方法,和屬性/變量無關

5.3 重寫和重載的區別

特性重載(Overload)重寫(Override)
定義在同一個類中,方法名相同但參數列表不同在子類中重新定義一個與父類中已定義的方法具有相同簽名、返回值一樣或這具有父子關系的方法
目的提供方法的不同實現,以適應不同的參數類型和數量改變或擴展父類方法的實現
訪問限定修飾符無要求子類方法的訪問權限必須大于等于父類方法
調用時機編譯時根據參數列表來確定調用哪個方法運行時確定
關鍵字無關鍵字可以借助@Override注解

5.4 動態綁定

上面講向上轉型時講過,當父類引用指向子類對象時,該父類引用只能訪問子類中繼承自父類的屬性和方法。那么,當向上轉型和方法重寫同時發生時,會碰撞出怎么樣的火花呢?

public class Animal {public int age;public String name;//父類的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重寫父類的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}
public class Test {public static void main(String[] args) {Animal dog = new Dog();dog.eat();}
}

在上述代碼中,引用指向子類的對象,再通過該父類引用去調用父類中被重寫的eat方法,按照我們之前學習的知識,此時運行結果應該是:Animal is eating.,但實際上:

運行結果:
Dog is eating.

這里可以得出一個結論:當父類引用去調用父類中被重寫的方法時,真正被調用的方法是子類中重寫的方法。
注意:

  • 1.具體最終調用哪個方法,在編譯時無法確定,在運行時根據對象的實際類型來確定
    在這里插入圖片描述
    在編譯時認為調用Animal中的eat方法,但是根據運行結果來看,實際上調用的是子類中重寫的eat方法
  • 2.在運行時確定調用的具體方法,這稱之為動態綁定/后期綁定,也是實現多態的基礎

6. 多態

6.1 多態的具體實現

public class Animal {public int age;public String name;//父類的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重寫父類的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}
public class Cat extends Animal {public int weight;//重寫父類的eat方法@Overridepublic Cat eat() {System.out.println("Cat is eating.");return null;}
}
public class Test {public static void func(Animal animal) {animal.eat();}public static void main(String[] args) {Dog dog = new Dog();Cat cat = new Cat();func(dog);func(cat);}
}

運行結果:
Dog is eating.
Cat is eating.

這里我們再回顧一下多態的概念:多態允許基類/父類的引用指向派生類/子類的對象(向上轉型),并通過該引用調用子類中重寫的方法,從而實現不同的行為。

在上述func方法中,同樣是通過animal形參來調用eat方法,兩個運行的結果發生了變化,這個過程就叫做多態

6.2 使用多態降低圈復雜度

什么叫圈復雜度?
圈復雜度是一種描述一段代碼復雜程度的方式。如果一段代碼平鋪直敘,那么就比較容易理解;如果一段代碼使用很多的條件分支或者循環語句,就認為代碼理解起來比較復雜。

我們可以簡單地舉個例子,一層if-else語句表示一圈復雜度,如果一段代碼的圈復雜度太高,就需要考慮重新構建該代碼的結果。一般來說,圈復雜度不應該超過10。
現在我們需要打印多個圖形

public class Shape {public void draw() {System.out.println("Shape");}
}
public class Flower extends Shape {@Overridepublic void draw() {System.out.println("flower");}
}
public class Rect extends Shape{@Overridepublic void draw() {System.out.println("rect");}
}
public class Circle extends Shape {@Overridepublic void draw() {System.out.println("circle");}
}

下面是不使用多態的代碼:

public class Test {public static void main(String[] args) {String[] array = new String[]{"flower","rect","circle"};//for(String cur : array) {if(cur.equals("flower")) {new Flower().draw();}else if(cur.equals("rect")) {new Rect().draw();}else {new Circle().draw();}}}
}

下面是使用多態的代碼:

public class Test {public static void main(String[] args) {Shape[] array = new Shape[]{new Flower(), new Rect(), new Circle()};for(Shape cur : array) {cur.draw();}}
}

6.3 避免在構造方法中調用重寫的方法

在這里插入圖片描述

實際執行結果:
Son0
期望執行結果:
Son10

上述代碼中,在父類的構造方法中調用func方法,此時調用的是子類中重寫的func方法。在子類的func方法中訪問了實例成員變量age,但是此時age還沒有開始初始化。因為此時仍然處于父類的構造方法中,而子類的實例成員變量初始化發生在父類構造方法結束之后。解決辦法有兩個:

  • 1.將age變量使用static修飾(不建議)
  • 2.避免在構造方法中調用重寫的方法(建議)

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

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

相關文章

GPS定位方案

目錄 一、常用的GPS定位方案包括: 二、主流品牌及熱銷型號 三、常用GPS算法及核心邏輯: 一、基礎定位算法 二、高精度算法 三、輔助優化算法 四、信號處理底層算法 四、基本原理(想自己寫算法的琢磨一下原理) 一、常用的GP…

PCIe - ZCU106(RC) + KU5P(EP) + 固化

目錄 1. 簡介 1.1 Data Mover 1.2 描述符 2. ZCU102 2.1 Ubuntu OS 2.2 USB Host 2.2.1 連接拓撲 2.2.2 設備類型 2.2.3 USB 跳帽設置 2.3 無線網卡 2.4 PCIe Info 2.4.1 Diagram 2.4.2 lspci -tv 2.4.3 lspci -v 2.4.2.1 設備基本信息 2.4.2.2 控制與狀態寄存…

精益數據分析(43/126):媒體網站商業模式的盈利與指標解析

精益數據分析(43/126):媒體網站商業模式的盈利與指標解析 在創業和數據分析的學習旅程中,我們不斷探索各種商業模式的奧秘,今天讓我們一同深入《精益數據分析》,聚焦媒體網站商業模式,剖析其盈…

Android數據庫全棧開發實戰:Room+SQLCipher+Hilt企業級應用構建

簡介 在移動應用開發中,數據庫作為數據存儲的核心組件,其安全性和性能對企業級應用至關重要。本文將從零開始,全面講解Android數據庫開發的最新技術,包括Room框架的深度使用、SQLCipher加密數據庫的實現、Hilt依賴注入的集成以及前后端數據同步的完整方案。通過一個加密任…

HarmonyOS 5.0 低時延音視頻開發??

大家好,我是 V 哥。 在HarmonyOS 5.0的開發中,支持低時延音視頻開發,為了確保語法正確, V 哥以下代碼符合HarmonyOS NEXT API 14的規范。為了方便初學者更好入門,V 哥伙同2位小伙伴花了1年時間,搞了三本鴻蒙…

微調大模型如何準備數據集——常用數據集,Alpaca和ShareGPT

微調大模型如何準備數據集——常用數據集,Alpaca和ShareGPT 數據集準備常用數據集自定義數據集AlpacaShareGPT數據集準備 常用數據集 預訓練數據集 Wiki Demo (en)RefinedWeb (en)RedPajama V2 (en)Wikipedia (en)Wikipedia (zh)Pile (en)

2025年OpenAI重大架構調整:資本與使命的再平衡

目錄 前言 一、調整核心:三重架構的重構 1.1 控制權的重新錨定 1.2 營利部門的角色轉型 1.3 資金池的重新配置 二、調整動因:三重矛盾的破解 2.1 資金需求與融資限制的沖突 2.2 商業競爭與使命純度的博弈 2.3 內部治理與外部監管的張力 三、產…

GD32/STM32 ADC/DMA使用指南

首先我們對ADC及DMA的基礎知識作一下簡單介紹。 一、 GD32/STM32 ADC模塊的核心要點 一)、ADC基礎特性 ?12位逐次逼近型? GD32/STM32 ADC為12位分辨率,最大量化值為4095(對應3.3V參考電壓),支持0-3.3V模擬輸入范…

Linux(十四)進程間通信(IPC),管道

一、進程間通信 (一)系統介紹進程間通信 進程間通信(IPC)介紹 小編插入的這篇文章詳細介紹了進程間通信的一些內容,大家可以一起學習。 (二)進程間通信的方法 1、管道 2、信號量 3、共享…

使用Homebrew下載配置git和連接GitHub(Mac版)

本文詳細介紹了在M系列Mac上安裝Homebrew并配置Git的過程,包括git的下載、設置全局用戶名和郵箱、生成SSH密鑰、添加GitHubSSH密鑰以及終端驗證。這些步驟有助于用戶順利進行協同開發。 一、下載git 1、終端輸入一下命令 brew install git2、這時下載完成 二、配…

懸崖邊的攝影牧歌

在云霧繚繞的懸崖邊,一場獨屬于自然與光影的邂逅悄然上演。 攝影師佇立于此,身旁是一群靈動的羊。他架起相機,眼神專注而熾熱,仿佛要將這天地間的一切都收納進小小的取景器。懸崖陡峭,巖石冷峻,卻因羊群的潔…

Linux環境基礎與開發工具使用

1. Linux編譯器vim 1.1 vim的基本概念講解 vim有很多種模式,我們初學者常用的就是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode)。 命令/正常模式(Normal mode) …

《Python星球日記》 第36天:線性代數基礎

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder😊) 專欄:《Python星球日記》,限時特價訂閱中ing 目錄 一、標量、…

使用VMware Workstation pro 17.5.1在Windows上安裝Ubuntu 24.04.2的 詳細步驟

一、準備工作 1. 下載Ubuntu 24.04.2 ISO鏡像 官方下載地址:Ubuntu 24.04.2 (Noble Numbat) 選擇 ubuntu-24.04.2-desktop-amd64.iso(桌面版)或 ubuntu-24.04.2-live-server-amd64.iso(服務器版)。 2. 確認系統要求…

ios systeam introduction

Here is an in-depth look at Apple’s iOS, from its inception to its latest major release, covering architecture, core components, security, app lifecycle, development tools, and the headline features of iOS 18. iOS began life as “iPhone OS,” unveiled alo…

優化04-選擇率和直方圖

選擇率 在Oracle數據庫中,選擇率(Selectivity) 是優化器(CBO,基于成本的優化器)用來評估SQL語句中某個條件(如WHERE子句)過濾數據的比例的關鍵指標。它直接影響優化器選擇執行計劃的…

python實戰:通過輸入文字匹配在docx文檔中的具體位置

在指定的docx文檔中,輸入一串文字來查看該文字在文檔中的具體位置;方便后續處理(如替換文字,高亮顯示等等操作) from docx import Documentdef find_text_in_docx(file_path, search_text):# 讀取docx文件doc = Document(file_path)# 遍歷段落,查找匹配的文本for i

Flutter——數據庫Drift開發詳細教程(二)

目錄 1.核心API1.1查詢數據列表分頁1.2 列表排序1.3推遲獲取與觀察 1.核心API 1.1查詢數據列表分頁 限制返回的結果數量limit&#xff0c;從某一位置開始查詢offset ///limit10, offset10 Future<List<TodoItem>> limitTodos(int limit, {int? offset}) {return …

mux-vlan基礎配置

1.top配置 2.各個交換機設置 sw3交換機的 sysname swb # undo info-center enable # vlan batch 10 20 30 100 # vlan 10description financial vlan vlan 20description marketing vlan vlan 30description client vlan vlan 100description principal vlanmux-vlansubordi…

SAM詳解2(初級應用)

SAM SAM5. 初級應用5.1 靜態本質不同子串個數5.2 字符串匹配5.3 關于子串出現次數5.4 動態添加時本質不同子串個數SAM 5. 初級應用 記 l o n g e s t ( x ) longest(x) longest(x) 為點 x x x 代表子串集合中最長串的長度。記 s h o r t e s t ( x ) shortest(x) shortest(…