<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 多態 [TOC] ## 導學 本章節,我們將要學習面向對象三大特性之一的多態,所謂的多態,字面意義來看就是多種形態。它是面向對象程序設置的最核心的特征。從某種意義上來講,封裝和繼承都是為了多態而準備的。 ## 多態的概念 在現實生活中,動物都有吃東西,跑和跳等通用的行為能力,但是不同的動物針對行為的表現形式是不同的。比如貓,狗,兔子喜歡吃的東西各有不同,而叫聲也是不一樣的。 ![](https://img.kancloud.cn/b7/a5/b7a58db8f8d7136f6c1c82757a3b851b_873x294.png) 再比如鍵盤上的`f1`鍵,在eclipse的界面會喚出eclipse的幫助文檔,在word的界面會喚出word的幫助文檔,在Windows系統下回喚出Windows的幫助文檔。可以看到同樣的行為在不同的對象上會產生不同的形式結果,這就是生活中的多態。 在程序中: >[info]多態意味著允許不同類的對象對同一消息作出不同的響應。 在Java中,多態在廣義上來說可以分為編譯時多態(方法的多態性)和運行時多態(對象的多態性)。 ~~~ 1. 編譯時多態(也叫設計時多態,通過方法重載實現) 2. 運行時多態(程序運行時動態決定調用哪個方法) ~~~ 在Java中指的多態,大多指的是運行時多態(狹義上的多態)。 多態實現的具體條件在于: 1. 滿足繼承關系 2. 父類引用指向子類對象 那么,什么是父類引用指向子類對象呢?接下來通過具體代碼來看看吧 ## 多態的實現 ### 場景描述及實體類編寫 ![](https://img.kancloud.cn/da/d1/dad1096b80c1396ddfe083da30a74b28_741x448.png) ~~~ package com.dodoke.proJ.animal; public class Animal { //屬性:昵稱、年齡 private String name; private int month; //方法:吃東西 public void eat() { System.out.println("動物都有吃東西的能力"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public Animal(String name, int month) { super(); this.name = name; this.month = month; } public Animal() { super(); } } ~~~ ~~~ package com.dodoke.proJ.animal; public class Cat extends Animal { //屬性:體重 private double weight; //方法:跑動 public void run() { System.out.println("小貓快樂的奔跑"); } //方法:吃東西 @Override public void eat() { System.out.println("貓吃魚~"); } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public Cat() { super(); } public Cat(String name, int month, double weight) { super(name, month); this.weight = weight; } } ~~~ ~~~ package com.dodoke.proJ.animal; public class Dog extends Animal{ //屬性:性別 private String sex; //方法:睡覺 public void sleep() { System.out.println("小狗有午睡的習慣"); } //方法:吃東西 @Override public void eat() { System.out.println("狗吃肉~"); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Dog() { super(); } public Dog(String name, int month, String sex) { this.setMonth(month); this.setMonth(month); this.sex = sex; } } ~~~ **多態是指編譯時類型和運行時類型不一致** >[warning] Java引用類型變量有兩種類型:一種是編譯時類型,一種是運行時類型 * 編譯的類型是由聲明變量的時候確定的 * 運行時的類型是由實際上賦給該變量的對象決定的 ~~~java //比如聲明變量類型為Animal,實際賦給該變量的對象類型為Dog Animal animal = new Dog(); ~~~ ### 向上轉型 **向上轉型是指父類引用指向子類的實例** 通常也稱向上轉型為隱式轉型和自動轉型。通俗來說,是子類轉型為父類,小類轉型為大類。 ~~~ public class Test { public static void main(String[] args) { //animal的引用 = 具體的animal實例 Animal one = new Animal(); Animal two = new Cat(); Animal three = new Dog(); one.eat(); two.eat(); three.eat(); one.getMonth(); two.getName(); //three.sleep(); } } ~~~ >[success]父類引用指向子類實例,可以調用子類重寫父類的方法以及父類派生的方法,但是無法調用子類獨有方法! >[warning]父類中的靜態方法無法被重寫,所以向上轉型后,只能調用到父類原有的靜態方法 ### 向下轉型 **向下轉型指的是子類引用指向父類對象** 向下轉型又稱之為強制類型轉換,通俗來講就是父類轉型為子類,大類轉為小類。對比之前的基本數據類型轉換,數據范圍大的類型轉為數據范圍小的類型就需要進行強制類型轉換,引用數據類型也是如此。 ~~~ public class Test { public static void main(String[] args) { //animal的引用 = 具體的animal實例 Animal one = new Animal(); Animal two = new Cat(); Animal three = new Dog(); one.eat(); two.eat(); three.eat(); one.getMonth(); two.getName(); //three.sleep(); System.out.println("======================================="); Cat temp = (Cat)two; temp.eat(); temp.run(); temp.getMonth(); /** * 此處的代碼不能進行強制類型轉換 * 原因:two這個對象定義的時候,實際上指向的是Cat這個類型的空間 * Cat temp = (Cat)two; 相當于把two對象還原成原來的實例空間。 * 但是狗和貓并沒有兼容的關系,只是擁有同一個父類而已,是兄弟關系 */ Dog temp1 = (Dog)two;//必須滿足轉型條件才能轉換 temp1.eat(); temp1.sleep(); temp1.getMonth(); System.out.println("程序繼續執行"); } } ~~~ **比較:** | 向上轉型 | 向下轉型 | | --- | --- | | 又稱隱式轉型、自動轉型 | 又稱強制類型轉換 | | 父類引用指向子類實例,可以調用子類重寫父類的方法以及父類派生的方法,無法調用子類獨有方法 | 子類引用指向父類對象,此處必須進行強制轉換,可以調用子類特有的方法 | | 小類轉為大類 | 必須滿足轉型條件才能進行轉換 | ### instanceof關鍵字 在之前的章節中,我們提到向下轉型需要滿足轉型條件,那么我們該如何才能知道是否滿足轉型條件呢 這時候,`instanceof`關鍵字就派上用處了! ![](https://img.kancloud.cn/b5/43/b543bd6b605589aa92400752114e07e3_556x159.png) `instanceof`關鍵字用于判斷左邊的對象是否滿足右邊的實例。如果滿足返回`true`,否則返回`false`。 ~~~ public class Test { public static void main(String[] args) { //animal的引用 = 具體的animal實例 Animal one = new Animal(); Animal two = new Cat(); Animal three = new Dog(); one.eat(); two.eat(); three.eat(); one.getMonth(); two.getName(); //three.sleep(); System.out.println("======================================="); if(two instanceof Cat) { Cat temp = (Cat)two; temp.eat(); temp.run(); temp.getMonth(); System.out.println("two可以轉換為Cat類型"); } if(two instanceof Dog) { Dog temp1 = (Dog)two; temp1.eat(); temp1.sleep(); temp1.getMonth(); System.out.println("two可以轉換為Dog類型"); } if(two instanceof Animal) { System.out.println("Animal"); } if(two instanceof Object) { System.out.println("Object"); } //所以two對象具有Animal類型和Object類型的特征 System.out.println("程序繼續執行"); } } ~~~ ### 類型轉換案例 #### 案例 在原有的項目中,增加一個主人類。在貓類中添加玩線球方法 ~~~ public class Master { /** * 喂寵物: * 喂貓咪:吃完東西后,主人會帶著去玩線球 * 喂狗狗:吃完東西后,主人會帶著去睡覺 * 養兔子、樣鸚鵡、養烏龜 */ //方案一:編寫方法,傳入不同類型的動物,調用各自的方法 public void feed(Cat cat) { cat.eat(); cat.palyBall(); } public void feed(Dog dog) { dog.eat(); dog.sleep(); } //方案二:編寫方法傳入動物的父類,方法中通過類型轉換,調用指定的子類的方法 public void feed(Animal ani) { if(ani instanceof Cat) { Cat cat = (Cat)ani; cat.eat(); cat.palyBall(); } else if(ani instanceof Dog) { Dog dog = (Dog)ani; dog.eat(); dog.sleep(); } } } ~~~ 多個對象要實現一個方法吃的時候,一般的解決方法是寫重載函數。 而新的解決方法是用一個方法傳參的時候先向上轉化成父類, 然后再根據實際情況進行判斷后,轉化成原來的類型 ,在調用自己本身的方法 (向下轉型) #### 新增需求 針對主人空閑時間的判斷,如果時間多則養狗,如果時間少則養貓。 ~~~ package com.dodoke.proJ.animal; public class Master { //方案一 public Dog hasManyTime() { System.out.println("主人空閑時間充足,適合養狗狗"); return new Dog(); } public Cat hasLittleTime() { System.out.println("主人空閑時間比較少,適合養貓咪"); return new Cat(); } //方案二 public Animal raise(boolean isManyTime) { if(isManyTime) { System.out.println("主人空閑時間充足,適合養狗狗"); return new Dog(); } else { System.out.println("主人空閑時間比較少,適合養貓咪"); return new Cat(); } } } ~~~ 在方法內部實現多態。返回值為子類對象,由父類引用接收。相當于Animal animal=new Cat();向上轉型。 當我們在實際開發當中需要同一個操作行為,針對不同的參數, 返回不同的實例對象,完成不同的操作結果的時候,就比較適用于這種多態的操作。類型轉換的優勢就會充分體現出來了。 ## 抽象類 在之前的代碼中,我們創建了一個動物類。接下來,我們來看看這段代碼 ~~~ Animal pet = new Animal("花花",2); pet.eat(); ~~~ 針對于這樣的代碼,編譯運行都沒有什么問題,但是它在實際的開發中并沒有有效的意義。每個動物都應該有具體的吃東西的行為,實例化pet對象沒有意義。實際的開發中會指代具體的貓還是狗來完成指代。如上的代碼并不符合程序的邏輯。 那么,該如何限制程序員寫這些沒有意義的代碼呢? 我們可以使用`abstract`關鍵字來限制類的實例化。 >[danger]`abstract` 是不能修飾成員變量的; ### 抽象類 ~~~ public abstract class Animal {} abstract public class Animal {} ~~~ 抽象數據類型: 在類的定義前加上 `abstract` 關鍵字,使得該類成為一個抽象類 **抽象類不允許被實例化**,但是可以通過向上轉型,指向子類實例,調用子類重寫父類的方法以及父類派生的方法 `public` 與 `abstract `可以互換,但是卻不能 與class關鍵字相互換位。 抽象類利用子類與父類的繼承關系,既限制了子類的設計隨意性,又避免了父類無意義的實例化。 >[info]應用場景:某個父類只是知道其子類應該包含怎樣的方法,但無法準確知道這些子類如何實現這些方法 ### 抽象方法 ~~~ public abstract class Animal { //屬性:昵稱、年齡 private String name; private int month; /** * 抽象方法:沒有方法體,在子類中必須重寫抽象方法,如果子類不重寫,則子類只能設置為抽象類 */ public abstract void eat(); public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public Animal(String name, int month) { super(); this.name = name; this.month = month; } public Animal() { super(); } } ~~~ 含有抽象方法的類,只能被定義為抽象類。抽象類中不一定含有抽象方法。 在抽象類中的成員方法可以包括一般方法和抽象方法。 抽象類不能實例化,即使抽象類中不包含抽象方法,這個抽象類也不能創建實例。 一個類繼承抽象類后,必須實現其所有的抽象方法,否則也是抽象類,不同的子類對父類的抽象方法可以有不同的實現。 >[warning]如果方法定義為 static,就不能使用 abstract 修飾符; 如果方法定義為 private ,也不能使用 abstract 修飾符; 抽象類體現的是一種模板模式的設計思想,抽象類作為多個子類的通用模板,子類在抽象的基礎上進行擴充,但是子類整體上會保留抽象類的行為方法(必須要實現抽象類的抽象方法)。 抽象類一般只是定義需要使用的方法,把不能實現的部分抽象成抽象方法,留給子類去實現。 父類中可以有實現的方法,但是子類也是可以對已經實現的方法進行改造的(override),但是如果在子類中還需要調用父類的實現方法,可以使用 super 關鍵字。 ## 練習 一、選擇 1. 下列代碼的運行結果為: ![](https://img.kancloud.cn/89/ac/89ac6b8d759d160b8263f6ca0729dcb3_435x399.png) ~~~ A. 我是動物 B. 編譯錯誤 C. 我是動物 我是老虎 我是哈士奇 D. 我是動物 我是老虎 我是哈士奇 ~~~ 2. 創建一個父類Animal,一個子類Cat,Animal three = new Cat();不是 ~~~ A. 向上轉型 B. 自動轉型 C. 向下轉型 D. 隱式轉型 ~~~ 3. 下列代碼怎么修改可以使其成功運行: ![](https://img.kancloud.cn/a7/9b/a79be55a9730187db6524886d2a9321b_520x567.png) ~~~ A. 刪除掉標注3位置的one.fly( ) B. 標注1的Animal one=new Bird()修改為Animal one=new Animal() C. 刪除掉標注2位置的one.eat( ) D. 標注1的Animal one=new Bird()修改為Bird one=new Animal() ~~~ 4. 下列關于instanceof說法不正確的是 ~~~ A. instanceof 的返回值為true和false B. instanceof可以用來判斷對象是否可滿足某個特定類型 C. 可以通過“A instanceof B"表示 A 類可以轉型為B類 D. instanceof可放在if語句的條件表達式中 ~~~ 5. 已知父類Person,子類Man。判斷類Person的對象person1是否滿足類Man的實例特征,正確的語句為 ![](https://img.kancloud.cn/50/b1/50b123a87e21e388ba7ddfef2aacfbe7_425x87.png) ~~~ A. if (person1? instanceof? Man) B. if (man1? instanceof? Person) C. if (Person? instanceof? man1) D. if (Man? instanceof? person1) ~~~ 6. 在Java中,多態的實現不僅能減少編碼的工作量,還能大大提高程序的可維護性及可擴展性,那么實現多態的步驟包括以下幾個方面除了 ~~~ A. 子類重寫父類的方法 B. 子類方法設置為final類型 C. 定義方法時,把父類類型作為參數類型;調用方法時,把父類或子類的對象作為參數傳入方法 D. 運行時,根據實際創建的對象類型動態決定使用哪個方法 ~~~ 7. 下面代碼運行測試后,出現的結果是 ![](https://img.kancloud.cn/66/51/66512c98be0ed19fdbef89de126ba6e6_387x563.png) ~~~ A. 編譯錯誤,錯誤位置在第一行 B. 編譯錯誤,錯誤位置在第二行 C. 第一行和第二行都運行成功,輸出結果為 兒子 女兒 D. 編譯成功,但運行報錯,錯誤位置在第二行 ~~~ 8. 下面代碼怎么修改可以編譯時不報錯(多選) ![](https://img.kancloud.cn/b9/bd/b9bdea0745ee05e327bb26e876b14343_785x424.png) ~~~ A. 在位置一處將SpeedBike類設為抽象類,同時將位置2處的speedup也設為抽象方法 B. 將位置一中的public改為final C. 將位置二中speedup()方法改為抽象方法 D. 將位置二中speedup()方法中加入方法的實現 ~~~ 9. 下列選項中,關于Java的抽象類和抽象方法說法不正確的是 ~~~ A. 抽象類和抽象方法都通過abstract關鍵字來修飾 B. 抽象類中必須包含抽象方法 C. 抽象方法只有方法聲明,沒有方法實現 D. 子類如果不重寫父類所有的抽象方法,則必須設置為抽象類 ~~~ 二、編程 1. 應用繼承和多態的思想,編寫動物類,成員方法是動物叫聲。寫三個具體的類(貓、狗、羊),它們都是動物類的子類,并重寫父類的成員方法。編寫測試類,隨機產生三種具體動物,調用叫聲這個方法。 程序參考運行效果如圖所示: ![](https://img.kancloud.cn/09/8f/098f70e79903745d9837952f40e47151_252x136.png) **任務分析:** **1.??? ? 定義一個父類Animal類** 屬性:kind(種類) 方法:創建帶參(kind為參數)構造方法 ? ? ? ? ? ? 創建cry():void方法 **2.??? ? 編寫三個具體的子類Cat類、Dog類、Sheep類** 分別重寫父類中的 cry() 方法,輸出信息分別為 Cat類:小貓的叫聲:喵喵喵~~~ Dog類:小狗的叫聲:汪汪汪~~~ Sheep類:小羊的叫聲:咩咩咩~~~ **3.??? ? 編寫測試類,首先生成長度為5的父類對象數組,然后通過循環依次向數組中存入數據,現設定存儲規則為:** a)??? ? 每次隨機產生一個0~2的正整數 b)?? ? 若數值為 0,則生成一個 Cat 類的對象,存入數組 c)??? ? 若數值為 1,則生成一個 Dog 類的對象,存入數組 d)?? ? 若數值為 2,則生成一個 Sheep 類的對象,存入數組 最后循環輸出數組成員,并分別調用 cry() 方法。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看