日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

允许使用抽象类类型 isearchboxinfo 的对象_Java面向对象编程三大特征 - 多态

發布時間:2025/4/5 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 允许使用抽象类类型 isearchboxinfo 的对象_Java面向对象编程三大特征 - 多态 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java面向對象編程三大特征 - 多態

本文關鍵字:Java、面向對象、三大特征、多態

多態是面向對象編程的三大特征之一,是面向對象思想的終極體現之一。在理解多態之前需要先掌握繼承、重寫、父類引用指向子類對象的相關概念,對繼承還沒有完全明白的同學可進傳送門: Java面向對象編程三大特征 - 繼承 。

一、抽象類

在繼承中,我們已經了解了子父類的關系以及如何對子父類進行設計,如果已經存在多個實體類,再去定義父類其實是不斷的抽取公共重合部分的過程,如果有需要將會產生多重繼承關系。在抽取整理的過程中,除了屬性可以復用,有很多方法一樣也可以復用,假如以圖形舉例:矩形、圓形,都可以具有周長和面積兩個方法,但是計算的方式完全不同,矩形和圓形之間肯定不能構成子父類關系,那么只能是同時去繼承一個父類,那么問題就來了,這兩個類都有什么共同點?

除了都是圖形好像并沒有什么共同點,矩形有兩組邊長,圓形是通過半徑來描述,如果非要往一起聯系的話。。。Wait a moment(靈光一閃中,請勿打擾)!!!難道說是都可以計算出周長和面積?細細想來,也是能說出一番道理的,但是這好抽象啊!
如果真的是這樣,也只能有一個模糊的思路,既然描述圖形的屬性不能夠共用那就分別放在兩個子類中吧,那么計算周長和面積的方法要怎么搞?如果在父類中定義相應的方法,那參數列表怎么寫?方法體怎么填?這個坑好像有點大,接下來,我們就要華麗地將這個坑填平。

1. 抽象與抽象類

在上面的例子中,我們遇到了一個情況,有兩個在邏輯上看似相關的類,我們想要把他們聯系起來,因為這樣做可以提高效率,但是在實施的過程中發現這個共同點有點太過模糊,難以用代碼描述,甚至于還不如分開用來的方便,這時就要引出抽象的概念,對應的關鍵詞為:abstract。

  • abstract可以修飾方法,修飾后被稱為抽象方法
  • abstract可以修飾類,修飾后被稱為抽象類
  • abstract不能與static修飾符同時使用

那么使用了abstract又能如何呢?這代表指定的方法和類很難表述,那么。。。就不用表述了!對于矩形類(Rectangle)與圓形類(Circle)的父類:圖形類(Figure),我們只能總結出他具有計算周長和面積的方法,而具體的實現方法我們無法給出,只有明確了圖形之后,才能給出具體的實現,于是我們使用抽象來描述這兩個方法, 被abstract修飾的方法不需要有方法體,且不能為private ,由于抽象方法沒有方法體,那么如果被代碼調用到了怎么辦呢?以下兩個限制規則可以杜絕這個問題:

  • 抽象方法只能存在于抽象類中(接口在另外的文章中討論)
  • 抽象類無法被直接實例化(匿名內部類的用法暫不做討論)

既然抽象類不能被實例化,那么自然也就不會調用到沒有方法體的那些方法了,那這些方法該怎么被調用呢?我們需要一步一步的來梳理,至少目前我們已經能夠清晰的得到如下的關系圖了:

2. 抽象類的特點

抽象類的本質依然是一個類(class),所以具備著一個普通類的所有功能,包括構造方法等的定義,總結一下,抽象類具有以下的幾個特點:

  • 抽象類由abstract修飾
  • 抽象類中允許出現抽象方法
  • 抽象類不能通過構造器直接實例化
  • 可以在抽象類中定義普通方法供子類繼承

現在,我們已經可以將抽象父類用代碼描述出來:

// 定義抽象類:圖形類 public abstract class Figure{// 定義計算周長的抽象方法:getC()public abstract double getC();// 定義計算面積的抽象方法:getS()public abstract double getS();// 定義描述圖形的非抽象方法:print()public void print(){System.out.println("這是一個圖形");} }

3. 天生的父類:抽象類

現在我們已經有了一個抽象類,其中也定義了抽象方法,抽象類不能被直接實例化保證了抽象方法不會被直接調用到。回憶一下我們的出發點,費勁巴力的弄出個抽象類就是為了提取出兩個類比較抽象的共同點,那么下一步自然是繼承了。

  • 抽象類不能直接實例化,是天生的抽象類
  • 如果一個類繼承了抽象類,那么必須重寫父類中的抽象方法
  • 如果抽象類中定義了構造方法,可以被子類調用或在實例化子類對象時執行
  • 如果抽象類的子類依然是抽象類,可以不重寫抽象方法,將重寫操作留給下一級子類

二、重寫

重寫指的是子父類之間方法構成的關系,當子類繼承父類時,父類中可能已經存在了某些方法,那么子類實例就可以直接進行調用。在有些時候由于子父類之間的差異,對于已經存在的方法想要做一些修改,這個時候我們可以利用重寫, 在子類中定義一個與父類中的方法完全相同的方法,包括返回值類型和方法簽名(方法名 + 參數列表) ,此時就會構成重寫。這樣,子類實例在調用方法時就可以覆蓋父類中的方法,具體的過程在后半部分闡述。

1. 重寫與重載的區別

我們在剛開始接觸方法的時候了解到了一個概念:重載,與重寫有些類似,容易混淆,如果知識點已經模糊可以進傳送門:Java程序的方法設計。總結一下,重寫和重載有以下區別:

  • 重載是同一個類中方法與方法之間的關系
  • 重寫是子父類間(接口與實現類間)方法與方法之間的關系
  • 構成重載:方法名相同,參數列表不同,返回值類型可以不同
  • 構成重寫:方法名相同,參數列表相同,返回值類型相同或為對應類型的子類
  • 構成重載的方法之間權限修飾符可以不同
  • 重寫方法的權限修飾符一定要大于被重寫方法的權限修飾符

有關于權限修飾符的作用如果不明確可以進傳送門: Java面向對象編程三大特征 - 封裝 。明確了重寫的含義之后,我們終于可以再度提筆,完成我們之前的例子:

// 定義矩形類 public class Rectangle extends Figure{// 定義構造器public Rectangle(double height, double width) {this.height = height;this.width = width;}// 定義長和寬public double height;public double width;// 重寫計算周長方法@Overridepublic double getC() {return 2 * (this.height + this.width);}// 重寫計算面積方法@Overridepublic double getS() {return this.height + this.width;}// 可選覆蓋@Overridepublic void print(){System.out.println("矩形");} } // 定義圓形類 public class Circle extends Figure{// 定義構造器public Circle(double radius) {this.radius = radius;}// 定義半徑public double radius;// 重寫計算周長方法@Overridepublic double getC() {return 2 * Math.PI * this.radius;}// 重寫計算面積方法@Overridepublic double getS() {return Math.PI * Math.pow(this.radius, 2);}// 可選覆蓋@Overridepublic void print(){System.out.println("圓形");} }

2. 方法重寫的規則

  • 重寫的標識為@Override
  • 方法的重寫發生在子類或者接口的實現類中
  • 被final聲明的方法不能被重寫
  • 被static聲明的方法不能被重寫,只能聲明同結構的靜態方法,但是此時不構成重寫
  • 受限于權限修飾符,子類可能只能重寫部分父類中的方法

3. 父類方法的顯式調用

從上面的代碼中可以看到,子類繼承父類后,如果存在抽象方法則比如重寫,由于父類中的方法是抽象的,所以無法調用。對于普通的方法,可以選擇性的重寫,一旦重寫我們可以認為父類的方法被覆蓋了,其實這樣的形容是不準確的,在初學階段可以認為是覆蓋。

比較規范的說法是:通過子類實例無法直接調用到父類中的同名方法了,但是在內存中依然存在著父類方法的結構,只不過訪問不到而已。另外,我們同樣可以在子類中顯式的調用出父類方法,這要用到super關鍵字。

  • super指代父類對象
  • super可以調用可訪問的父類成員變量
  • super可以調用可訪問的父類成員方法
  • super可以調用可訪問的父類構造方法
  • 不能使用super調用父類中的抽象方法
  • 可以使用super調用父類中的靜態方法

如果我們需要在子類中調用父類方法或構造器,可以將代碼修改如下:

// 定義抽象類:圖形類 public abstract class Figure{// 在抽象類中定義構造器,在子類實例創建時執行public Figure(){System.out.println("Figure init");}// 定義計算周長的抽象方法:getC()public abstract double getC();// 定義計算面積的抽象方法:getS()public abstract double getS();// 定義描述圖形的非抽象方法:print()public void print(){System.out.println("這是一個圖形");} } // 定義矩形類 public class Rectangle extends Figure{// 定義構造器public Rectangle(double height, double width) {super();// 會調用默認的無參構造,代碼可省略this.height = height;this.width = width;}// 定義長和寬public double height;public double width;// 重寫計算周長方法@Overridepublic double getC() {return 2 * (this.height + this.width);}// 重寫計算面積方法@Overridepublic double getS() {return this.height + this.width;}// 可選覆蓋@Overridepublic void print(){super.print();// 調用父類方法System.out.println("矩形");} } // 定義圓形類 public class Circle extends Figure{// 定義構造器public Circle(double radius) {super();// 會調用默認的無參構造,代碼可省略this.radius = radius;}// 定義半徑public double radius;// 重寫計算周長方法@Overridepublic double getC() {return 2 * Math.PI * this.radius;}// 重寫計算面積方法@Overridepublic double getS() {return Math.PI * Math.pow(this.radius, 2);}// 可選覆蓋@Overridepublic void print(){super.print();// 調用父類方法System.out.println("圓形");} }

三、父類引用指向子類對象

前面提到的概念消化完畢后,我們看一下子父類對象實例化的形式以及方法的執行效果。

1. 父類引用指向父類對象

如果父類是一個抽象類,則在等號右側不能直接使用new加構造方法的方式實例化,如果一定要得到父類實例,就要使用匿名內部類的用法,這里不做討論。

如果父類是一個普通類,那么我們在初始化時,等號左側為父類型引用,等號右側為父類型對象(實例),這個時候其實和我們去創建一個類的對象并沒有什么分別,不需要想著他是某某類的父類,因為 此時他不會和任何子類產生關系,只是一個默認繼承了Object類的普通類 ,正常使用就好,能調用出的內容也都是父類中已定義的。

2. 子類引用指向子類對象

在進行子類實例化時,由于在子類的定義中繼承了父類,所以在創建子類對象時,會先一步創建父類對象。在進行調用時,根據權限修飾符,可以調用出子類及父類中可訪問的屬性和方法。

public class Test{public static void main(String[] args){Rectangle rectangle = new Rectangle(5,10);// 調用Rectangle中定義的方法,以子類重寫為準rectangle.print();System.out.println(rectangle.getC());// 得到矩形周長System.out.println(rectangle.getS());// 得到矩形面積Circle circle = new Circle(5);// 調用Circle中定義的方法,以子類重寫為準circle.print();System.out.println(circle.getC());// 得到圓形周長System.out.println(circle.getS());// 得到圓形面積} }

3. 引用與對象之間的關系

在剛開始學習編程時,我們接觸了基本數據類型,可以直接用關鍵字聲明,定義變量賦值后使用,并不需要使用new關鍵字。對于引用與對象的關系可以先參考之前的文章回顧一下: Java中的基本操作單元 - 類和對象 。在這里我們重點要說明的是:等號左側的引用部分,與等號右側的部分在程序運行層面有怎樣的關聯。

與基本數據類型不同,在類中可以定義各種屬性和方法,使用時也需要先創建對象。等號左側的部分依然是一個類型的聲明,未賦值時雖然默認情況下是null,但在程序編譯運行時,也會在棧中進行存儲,記錄了相應的結構信息,他所指向的對象必須是一個和它 兼容 的類型。

類的聲明引用存放在棧中,實例化得到的對象存放在堆中。

  • 在代碼編寫階段,能夠調用出的內容以等號左側類型為準
  • 在程序運行階段,具體的的執行效果以等號右側實例為準

下圖為引用與實例在內存中的關系示意圖,有關于Java對象在內存中的分布將在另外的文章中說明:

4. 父類引用指向子類對象

了解了引用與對象的關系之后,就有了一個疑問,如果等號左側的聲明類型與等號右側的實例類型不一致會怎么樣呢?如果我們要保證程序能夠通過編譯,并且順利執行,必須要保證等號兩邊的類型是兼容的。完全不相關的兩個類是不能夠出現在等號左右兩邊的,即使可以使用強制類型轉換通過編譯,在運行時依然會拋出異常。

于是我們就聯想到了子父類是否有可能進行兼容呢?會有兩種情況:子類引用指向父類對象,父類引用指向子類對象,下面我們來一一討論。

  • 子類引用指向父類對象為什么無法使用

子類引用指向父類對象指的是:等號左側為子類型的聲明定義,等號右側為父類型的實例。首先,結論是這種用法是不存在的,我們從兩方面來分析原因。

第一個方面,是否符合邏輯?也就是是否會有某種需求,讓Java語言為開發者提供這樣一種用法?顯然是否定的,我們定義子類的目的就是為了擴展父類的功能,結果現在我們卻在用老舊的、功能貧乏的父類實例(等號右側)去滿足已經具備了強勁的、功能更為強大的子類聲明(等號左側)的需要,這顯然是不合理的。

另一方面,在程序運行時是否能夠辦到?如果我們真的寫出了相關的代碼,會要求我們添加強制轉換的語句,否則無法通過編譯,即使通過,在運行時也會提示無法進行類型轉換。這就相當于把一個只能打電話發短信的老人機強制轉換為能安裝各種APP的智能機,這顯然是辦不到的。

  • 父類引用指向子類對象有什么樣的意義

父類引用指向子類對象指的是:等號左側為父類型的定義,等號右側為子類型的實例。這種情況是會被經常使用的,類似的還有:接口指向實現類。那么,這種用法應該如何解釋,又為什么要有這樣的用法呢?

首先,我們先來理解一下這代表什么含義,假如:父類為圖形,子類為矩形和圓形。這就好比我聲明了一個圖形對象,這個時候我們知道,可以調用出圖形類中定義的方法,由于圖形類是一個抽象類,是不能直接實例化的,我們只能用他的兩個子類試試看。

public class Test{public static void main(String[] args){// figure1指向Rectangle實例Figure figure1 = new Rectangle(5,10);System.out.println(figure1.getC());// 得到矩形周長System.out.println(figure1.getS());// 得到矩形面積// figure2指向Circle實例Figure figure2 = new Circle(5);System.out.println(figure2.getC());// 得到圓形周長System.out.println(figure2.getS());// 得到圓形面積} }

從上面的結果來看,這好像和子類引用指向子類對象的執行效果沒什么區別呀?但是需要注意此時使用的是父類的引用,區別就在于,如果我們在子類中定義了獨有的內容,是調用不到的。在上面已經解釋了運行效果以等號右側的實例為準,所以結果與直接創建的子類實例相同并不難理解。

重點要說明一下其中的含義:使用Figure(圖形)聲明,代表我現在只知道是一個圖形,知道能執行哪些方法,如果再告知是一個矩形,那就能算出這個矩形的周長和面積;如果是一個圓形,那就能算出這個圓形的周長和面積。我們也可以這樣去描述:這個圖形是一個矩形或這個圖形是一個圓形。

如果從程序運行的角度去解釋,我們已經知道,子類對象在實例化時會先實例化父類對象,并且,如果子類重寫了父類的方法,父類的方法將會隱藏。如果我們用一個父類引用去指向一個子類對象,這就相當于對象實例很強大,但是我們只能啟用部分的功能,但是有一個好處就是 相同的指令,不同的子類對象都能夠執行,并且會存在差異 。這就相當于一部老人機,只具備打電話和發短信的功能,小米手機和魅族手機都屬于升級擴展后的智能機,當然保有手機最基本的通訊功能,這樣使用是沒問題的。

四、多態

學習了上面的內容后,其實你已經掌握了多態的用法,現在我們來明確總結一下。

1. 什么是多態

多態指的是同一個父類,或同一個接口,發出了一個相同的指令(調用了同一個方法),由于具體執行的實例(子類對象或實現類對象)不同,而有不同的表現形態(執行效果)。

就像上面例子中的圖形一樣,自身是一個抽象類,其中存在一些抽象方法,具體的執行可以由子類對象來完成。對于抽象類的抽象方法,由于子類必須進行重寫,所以由子類去執行父類的抽象方法必然是多態的體現,對于其他的情況則未必構成多態,因此總結了以下三個必要條件。

2. 多態的必要條件

  • 存在子父類繼承關系
  • 子類重寫父類的方法
  • 父類引用指向子類對象

只有滿足了這三個條件才能構成多態,這也就是文章前三點用這么長的篇幅來鋪墊的原因。

3. 多態的優點

使用多態有多種好處,特別是一個抽象類有多個子類,或一個接口存在多個抽象類時,在進行參數傳遞時就會非常的靈活,在方法中只需要定義一個父類型作為聲明,傳入的參數可以是父類型本身,也可以是對應的任意子類型對象。于是,多態的優點可以總結如下:

  • 降低耦合:只需要與父類型產生關聯即可
  • 可維護性(繼承保證):只需要添加或修改某一子類型即可,不會影響其他類
  • 可擴展性(多態保證):使用子類,可以對已有功能進行快速擴展
  • 靈活性
  • 接口性
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的允许使用抽象类类型 isearchboxinfo 的对象_Java面向对象编程三大特征 - 多态的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。