java多態的使用注意事項,java多態的使用注意事項是什麼

本文目錄一覽:

JAVA的多態有什麼特點?什麼地方適合使用多態?

多態性是指允許不同子類型的對象對同一消息作出不同的響應。簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。多態性分為編譯時的多態性和運行時的多態性。

如果將對象的方法視為對象向外界提供的服務,那麼運行時的多態性可以解釋為:當A系統訪問B系統提供的服務時,B系統有多種提供服務的方式,但一切對A系統來說都是透明的(就像電動剃鬚刀是A系統,它的供電系統是B系統,B系統可以使用電池供電或者用交流電,甚至還有可能是太陽能,A系統只會通過B類對象調用供電的方法,但並不知道供電系統的底層實現是什麼,究竟通過何種方式獲得了動力)。

方法重載(overload)實現的是編譯時的多態性(也稱為前綁定),而方法重寫(override)實現的是運行時的多態性(也稱為後綁定)。

談談你對Java中的多態的理解.(為什麼要使用多態,有什麼好處,一般用在什麼場合)

面向對象編程有三大特性:封裝、繼承、多態。

封裝隱藏了類的內部實現機制,可以在不影響使用的情況下改變類的內部結構,同時也保護了數據。對外界而已它的內部細節是隱藏的,暴露給外界的只是它的訪問方法。

繼承是為了重用父類代碼。兩個類若存在IS-A的關係就可以使用繼承。,同時繼承也為實現多態做了鋪墊。

那麼什麼是多態呢?多態的實現機制又是什麼?請看我一一為你揭開:

所謂多態

就是指程序中定義的引用變數所指向的具體類型和通過該引用變數發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變數倒底會指向哪個類的實例對象,該引用變數發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變數綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。

比如你是一個酒神,對酒情有獨鍾。某日回家發現桌上有幾個杯子裡面都裝了白酒,從外面看我們是不可能知道這是些什麼酒,只有喝了之後才能夠猜出來是何種酒。你一喝,這是劍南春、再喝這是五糧液、再喝這是酒鬼酒….在這裡我們可以描述成如下:

酒 a = 劍南春

酒 b = 五糧液

酒 c = 酒鬼酒

這裡所表現的的就是多態。劍南春、五糧液、酒鬼酒都是酒的子類,我們只是通過酒這一個父類就能夠引用不同的子類,這就是多態——我們只有在運行的時候才會知道引用變數所指向的具體實例對象。

誠然,要理解多態我們就必須要明白什麼是「向上轉型」。在繼承中我們簡單介紹了向上轉型,這裡就在啰嗦下:在上面的喝酒例子中,酒(Win)是父類,劍南春(JNC)、五糧液(WLY)、酒鬼酒(JGJ)是子類。我們定義如下代碼:

JNC a = new JNC();

對於這個代碼我們非常容易理解無非就是實例化了一個劍南春的對象嘛!但是這樣呢?

Wine a = new JNC();

在這裡我們這樣理解,這裡定義了一個Wine 類型的a,它指向JNC對象實例。由於JNC是繼承與Wine,所以JNC可以自動向上轉型為Wine,所以a是可以指向JNC實例對象的。這樣做存在一個非常大的好處,在繼承中我們知道子類是父類的擴展,它可以提供比父類更加強大的功能,如果我們定義了一個指向子類的父類引用類型,那麼它除了能夠引用父類的共性外,還可以使用子類強大的功能。

但是向上轉型存在一些缺憾,那就是它必定會導致一些方法和屬性的丟失,而導致我們不能夠獲取它們。所以父類類型的引用可以調用父類中定義的所有屬性和方法,對於只存在與子類中的方法和屬性它就望塵莫及了。

public class Wine {  

    public void fun1(){  

        System.out.println(“Wine 的Fun…..”);  

        fun2();  

    }  

      

    public void fun2(){  

        System.out.println(“Wine 的Fun2…”);  

    }  

}  

  

public class JNC extends Wine{  

    /** 

     * @desc 子類重載父類方法 

     *        父類中不存在該方法,向上轉型後,父類是不能引用該方法的 

     * @param a 

     * @return void 

     */  

    public void fun1(String a){  

        System.out.println(“JNC 的 Fun1…”);  

        fun2();  

    }  

      

    /** 

     * 子類重寫父類方法 

     * 指向子類的父類引用調用fun2時,必定是調用該方法 

     */  

    public void fun2(){  

        System.out.println(“JNC 的Fun2…”);  

    }  

}  

  

public class Test {  

    public static void main(String[] args) {  

        Wine a = new JNC();  

        a.fun1();  

    }  

}

————————————————-

Output:

Wine 的Fun…..

JNC 的Fun2…

從程序的運行結果中我們發現,a.fun1()首先是運行父類Wine中的fun1().然後再運行子類JNC中的fun2()。

分析:在這個程序中子類JNC重載了父類Wine的方法fun1(),重寫fun2(),而且重載後的fun1(String a)與 fun1()不是同一個方法,由於父類中沒有該方法,向上轉型後會丟失該方法,所以執行JNC的Wine類型引用是不能引用fun1(String a)方法。而子類JNC重寫了fun2() ,那麼指向JNC的Wine引用會調用JNC中fun2()方法。

所以對於多態我們可以總結如下:

指向子類的父類引用由於向上轉型了,它只能訪問父類中擁有的方法和屬性,而對於子類中存在而父類中不存在的方法,該引用是不能使用的,儘管是重載該方法。若子類重寫了父類中的某些方法,在調用該些方法的時候,必定是使用子類中定義的這些方法(動態連接、動態調用)。

對於面向對象而已,多態分為編譯時多態和運行時多態。其中編輯時多態是靜態的,主要是指方法的重載,它是根據參數列表的不同來區分不同的函數,通過編輯之後會變成兩個不同的函數,在運行時談不上多態。而運行時多態是動態的,它是通過動態綁定來實現的,也就是我們所說的多態性。

多態的實現

      2.1實現條件

在剛剛開始就提到了繼承在為多態的實現做了準備。子類Child繼承父類Father,我們可以編寫一個指向子類的父類類型引用,該引用既可以處理父類Father對象,也可以處理子類Child對象,當相同的消息發送給子類或者父類對象時,該對象就會根據自己所屬的引用而執行不同的行為,這就是多態。即多態性就是相同的消息使得不同的類做出不同的響應。

 Java實現多態有三個必要條件:繼承、重寫、向上轉型。

繼承:在多態中必須存在有繼承關係的子類和父類。

重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。

向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法。

只有滿足了上述三個條件,我們才能夠在同一個繼承結構中使用統一的邏輯實現代碼處理不同的對象,從而達到執行不同的行為。

對於Java而言,它多態的實現機制遵循一個原則:當超類對象引用變數引用子類對象時,被引用對象的類型而不是引用變數的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。

2.2實現形式

在Java中有兩種形式可以實現多態。繼承和介面。

      2.2.1、基於繼承實現的多態

基於繼承的實現機制主要表現在父類和繼承該父類的一個或多個子類對某些方法的重寫,多個子類對同一方法的重寫可以表現出不同的行為。

public class Wine {  

    private String name;  

      

    public String getName() {  

        return name;  

    }  

  

    public void setName(String name) {  

        this.name = name;  

    }  

  

    public Wine(){  

    }  

      

    public String drink(){  

        return “喝的是 ” + getName();  

    }  

      

    /** 

     * 重寫toString() 

     */  

    public String toString(){  

        return null;  

    }  

}  

  

public class JNC extends Wine{  

    public JNC(){  

        setName(“JNC”);  

    }  

      

    /** 

     * 重寫父類方法,實現多態 

     */  

    public String drink(){  

        return “喝的是 ” + getName();  

    }  

      

    /** 

     * 重寫toString() 

     */  

    public String toString(){  

        return “Wine : ” + getName();  

    }  

}  

  

public class JGJ extends Wine{  

    public JGJ(){  

        setName(“JGJ”);  

    }  

      

    /** 

     * 重寫父類方法,實現多態 

     */  

    public String drink(){  

        return “喝的是 ” + getName();  

    }  

      

    /** 

     * 重寫toString() 

     */  

    public String toString(){  

        return “Wine : ” + getName();  

    }  

}  

  

public class Test {  

    public static void main(String[] args) {  

        //定義父類數組  

        Wine[] wines = new Wine[2];  

        //定義兩個子類  

        JNC jnc = new JNC();  

        JGJ jgj = new JGJ();  

          

        //父類引用子類對象  

        wines[0] = jnc;  

        wines[1] = jgj;  

          

        for(int i = 0 ; i  2 ; i++){  

            System.out.println(wines[i].toString() + “–” + wines[i].drink());  

        }  

        System.out.println(“——————————-“);  

  

    }  

}

OUTPUT:

Wine : JNC–喝的是 JNC

Wine : JGJ–喝的是 JGJ

在上面的代碼中JNC、JGJ繼承Wine,並且重寫了drink()、toString()方法,程序運行結果是調用子類中方法,輸出JNC、JGJ的名稱,這就是多態的表現。不同的對象可以執行相同的行為,但是他們都需要通過自己的實現方式來執行,這就要得益於向上轉型了。

我們都知道所有的類都繼承自超類Object,toString()方法也是Object中方法,當我們這樣寫時:

Object o = new JGJ();

System.out.println(o.toString());

輸出的結果是Wine : JGJ。

Object、Wine、JGJ三者繼承鏈關係是:JGJ—Wine—Object。所以我們可以這樣說:當子類重寫父類的方法被調用時,只有對象繼承鏈中的最末端的方法才會被調用。但是注意如果這樣寫:

Object o = new Wine();

System.out.println(o.toString());

輸出的結果應該是Null,因為JGJ並不存在於該對象繼承鏈中。

所以基於繼承實現的多態可以總結如下:對於引用子類的父類類型,在處理該引用時,它適用於繼承該父類的所有子類,子類對象的不同,對方法的實現也就不同,執行相同動作產生的行為也就不同。

如果父類是抽象類,那麼子類必須要實現父類中所有的抽象方法,這樣該父類所有的子類一定存在統一的對外介面,但其內部的具體實現可以各異。這樣我們就可以使用頂層類提供的統一介面來處理該層次的方法。

 2.2.2、基於介面實現的多態

繼承是通過重寫父類的同一方法的幾個不同子類來體現的,那麼就可就是通過實現介面並覆蓋介面中同一方法的幾不同的類體現的。

在介面的多態中,指向介面的引用必須是指定這實現了該介面的一個類的實常式序,在運行時,根據對象引用的實際類型來執行對應的方法。

繼承都是單繼承,只能為一組相關的類提供一致的服務介面。但是介面可以是多繼承多實現,它能夠利用一組相關或者不相關的介面進行組合與擴充,能夠對外提供一致的服務介面。所以它相對於繼承來說有更好的靈活性。

java中的多態 到底怎麼使用

Java中的多態允許父類指針指向子類實例。如:Father obj=new Child();(其中Child是Father的子類)。這樣就產生了一個問題——

使用這個父類型的指針訪問類的屬性或方法時,如果父類和子類都有這個名稱的屬性或方法,哪一個屬性或方法會被調用呢?

最好的辦法是實驗:

class Father

{

int r;

Father()

{

r=4;

}

void printname()

{

System.out.println(“I』m father”);

}

}

class Child extends Father

{

int r;

Child()

{

r=5;

}

void printname()

{

System.out.println(“I』m Child”);

}

}

public class Test

{

public static void main(String[] args)

{

Father obj=new Child();

System.out.println(obj.r);

obj.printname();

}

}

結果輸出:

4

I』m Child

實驗證明。屬性會訪問父類的。方法分訪問子類的。

這就是多態了。

不要以為Father obj=new Child();這條語句一定會讓obj.printname()指向Chlid定義的printname()。實際上,如果你把Father類中的printname()方法刪除,這個程序將編譯失敗。因為Father中的並沒有定義printname()這個函數。多態是晚綁定*(見本文最後的資料)的,在Father obj=new Child();這個語句中,如果Father中沒有printname()這個函數,就不會為obj建立一個用於調用printname()函數的指針。所以調用obj.printname()會出錯。如果Father中有這個函數。指向printname()函數的指針會被創建。在調用obj.printname()時,不會出錯,而且,因為obj指向的是new Child(),是個Chld類的實例。所以調用obj.printname()時調用了Child類中定義的printname()。這就是方法的動態綁定。

那麼,剛才說到把Father類中的printname()方法刪掉後,obj將無法調用Child類中的printname(),因為obj.printname()會編譯失敗。那麼如果我就是需要調用要怎麼辦呢?其實雖然obj是Father類型的,但是它指向的是一個Child類的實例。那麼可以將obj強制類型轉換為Child。再調用printname()方法就可以了。

在上面程序中,把Father類中的printname()方法整個刪掉,再將obj.printname() 改成 ((Child)obj).printname()後,編譯成功,結果輸出:

4

I』m Child

兩次輸出的結果都是I』m Child。

那麼如何可以運行Child類中的printname()來輸出「I』m Father」呢?

其實只需要將Father obj=new Child();改成Father obj=new Father();就可以了,呵呵。另一個辦法就是將Child類中定義的printname()整個刪掉。為什麼這樣可以成功呢?自己想想,嘿嘿。最後會有個這樣的思考題。

看到這兒你可能早就想問了:

為什麼obj.r是4?為什麼不是5?

呵呵。其實很簡單。Java中的多態僅為方法而言,成員變數還是使用的父類的成員變數。也就是說,因為「Father obj =……」,所以obj是Father類型的,所以obj裡面的r是Father裡面的r,所以輸出obj.r就是4了。

你又想問:

那麼5去哪了?new Child()的時候,不是會把5放到Child的r中嗎?

實際上5還是有的。只是obj.r是4而已。想訪問Child中的r,把5讀出來,可以這樣寫:

((Child)obj).r

就是把obj由Father型強制轉換成了Child型。

OK,方法和屬性在多態中是什麼樣的你都清楚了。現在做個題測試一下吧:

這是J@Whiz1.4的一道題:

class Base {

int i = 99;

public void amethod() {

System.out.println(“Base.amethod()”);

}

Base() {

amethod();

}

}

public class Derived extends Base {

int i = -1;

public static void main(String argv[]) {

Base b = new Derived();

System.out.println(b.i);

b.amethod();

}

public void amethod() {

System.out.println(“Derived.amethod()”);

}

}

會輸出什麼?

先想想,再看答案:

答案:

========================

Derived.amethod()

99

Derived.amethod()

========================

講解:

這個程序的執行過程是這樣的:

第一行:Base b=new Derived();

執行這一行語句的過程中,要構造Derived這個類,而它有父類Base,所以先構造Base類。構造Base類的默認構造函數有定義。內容是執行amethod()方法。

實際上,Base類構造方法中的執行amethod(),相當於執行this.amethod(),在這個程序中,就相當於執行b.amethod()。而b是Base類型的,指向了Derived類的實例的指針。所以跟據上面我們的總結,實際上執行的是Derived類的amethod()函數。所以,第一行「Base b=new Derived();」執行完,輸出”Derived.amethod()”。

第二行:System.out.println(b.i);

這個很簡單,成員變數,不考慮多不多態,只看它定義時前面的類型。這個程序中是Base b,所以b.i就是Base類中的i。輸出99

第三行:b.amethod();

調用Derived類中的amethod()方法。

其實這行就是迷惑你的,如果沒有這一行。你可能會警覺起來——咦?為什麼這兒定義一個amethod()呢?沒有地方調用它啊?

有了這行代碼,就會使你放鬆警惕。覺得,啊。定義了這個是用來讓b.amethod();調用的。

Java中多態的優缺點是什麼?

多態的好處:擴展性強,父類的變數可以賦值不同的子類對象,而調用不同的子類重寫的方法

多態的弊端:不能使用子類特有的功能

這就是Java多態的優缺點了,想學習Java,自學看黑馬程序員,有視頻,有學習路線,讓你學習不迷茫!

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/191041.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-30 09:08
下一篇 2024-11-30 09:08

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • int類型變數的細節與注意事項

    本文將從 int 類型變數的定義、聲明、初始化、範圍、運算和類型轉換等方面,對 int 類型變數進行詳細闡述和講解,幫助讀者更好地掌握和應用 int 變數。 一、定義與聲明 int…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29

發表回復

登錄後才能評論