文章目錄
- 前言
- 一、多態
- 1.1 多態的概念
- 1.2 多態的實現條件
- 1.3 重寫
- 1.3.1方法重寫的規則
- 1.3.2重寫和重載的區別
- 1.4 向上轉型和向下轉型
- 1.4.1向上轉型
- 1.4.2向下轉型
- 1.5 多態的優缺點
- 1.5.1 使用多態的好處
- 1.5.2 使用多態的缺陷
- 結語
前言
為了深入了解JAVA的面向對象的特性,今天繼續來學習剖析多態。多態在Java中的應用非常廣泛,它在繼承的前提下讓子類將父類中的方法進行重寫,最后通過父類對象訪問到子類重寫的方法實現。它可以大大優化代碼篇幅,讓代碼整體更清晰整潔,同時更方便使用者進行代碼的迭代更新!
提到了很多新內容,本篇文章將會詳細介紹Java語言中的多態概念,重寫,向上轉型和向下轉型等相關內容,讓我們速速開始吧!!
看前提醒🚨:想要了解多態就先需要學習繼承,相關內容在該篇博客:【Java】面向對象之繼承超級詳解!!有詳細講解,學完再食用本篇博客效果更佳哦🌹🌹
一、多態
1.1 多態的概念
多態的概念:簡單來說,就是多種形態,去完成某個行為,當不同的對象去完成時會產生不同的狀態。
舉個栗子🌰!
自然界有各種動物,它們發出的聲音不盡相同,但都是做的發聲這個動作
總而言之:同一件事,發生在不同對象身上,就會產生不同的結果。
那語言環境中如何實現多態?
1.2 多態的實現條件
在Java中如要實現多態,則需要滿足以下三個條件,缺一不可:
- 必須要在繼承的條件下
- 子類必須要對父類中的相關成員方法進行重寫
- 通過父類的引用調用重寫的方法
我們來看一段代碼:
class Animal{String name;int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(name+"吃飯....");}
}
class Dog extends Animal{public Dog(String name,int age){super(name,age);}@Overridepublic void eat() {System.out.println(name + "正在吃狗糧.....");}
}class Cat extends Animal{public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃貓糧......");;}
}//*******************************************
public class test {static void eat(Animal A){A.eat();}public static void main(String[] args) {Dog dog = new Dog("沖沖",18);Cat cat = new Cat("JAVA",90);eat(dog);eat(cat);System.out.println("別忘了點贊三連支持歐o(>ω< )o!!!");}
}
//*******************
//運行結果:
//沖沖正在吃狗糧.....
//JAVA正在吃貓糧.....
我們可以看到同樣用eat方法最后輸出的內容卻不同。
當類的調用者在編寫eat
方法的時候,參數類型為Animal(父類)
,此時該方法的內部并不知道,也不關注當前的A
引用指向的是哪種類型(哪個子類)的實例,則此時A
引用調用eat
方法時就可能會有不同的結果表現,而這種思想行為就稱為多態。
1.3 重寫
在上面的實現條件中講到要對父類中的相關方法進行重寫,那么重寫又是什么呢?
重寫(Override):也稱之為覆蓋,覆寫。重寫是對子類對父類中類非靜態、非private修飾,非final修飾,非構造方法等的成員方法進行重新編寫。返回值,方法名和形參都不能改變! 即外殼不變,核心重寫。
重寫的好處在于能實現子類的需要,定義特定于自己的行為,也就是說子類能根據需要實現父類的方法。
1.3.1方法重寫的規則
- 子類在重寫父類對應方法時,一般必須與父類對應方法一致:返回值類型、形參(參數列表)、方法名
- 訪問權限不能比父類對應重寫方法的訪問權限更低。
例如:如果父類方法由protected修飾,則重寫的子類方法只能由protected或public修飾,否則重寫失敗😭 - 父類被static、private、final修飾的方法不能被重寫
@Override 注解
重寫方法時可使用該注解來顯式指定,有了該注解可以幫我們在編寫代碼時進行一些合法性校驗。
例如:編寫時不小心將eat
打成了aet
(出現了拼寫錯誤),那么此時編譯器就會發現父類中沒有aet
方法,就會編譯報錯,提示無法進行重寫。
1.3.2重寫和重載的區別
要點 | 重寫(Override) | 重載(overload) |
---|---|---|
參數列表 | 一定不能修改 | 必須修改 |
返回類型 | 一定不能修改【除非能構成父子關系】 | 可以修改 |
訪問限定符 | 一定不能做更嚴格的限制(可以降低限制) | 可以修改 |
【重寫的設計原則】
對于已經投入使用的類,盡量不要進行修改。最好的方法是:重復利用其中共性的內容,并且添加或者改動新的內容,實現迭代更新。
1.4 向上轉型和向下轉型
1.4.1向上轉型
實際上就是創建一個子類對象,將其當成父類對象來使用。
語法格式:父類類型 對象名 = new 子類類型()
Animal animal = new Cat("修貓",88);
animal是父類類型,但可以引用子類對象,因為是從大范圍中找小范圍的效果。
【使用方法】
-
直接賦值
public static void main(String[] args) {Dog dog = new Dog("沖沖",18);Animal animal1 = dog;//這就屬于直接賦值animal1.eat();Cat cat = new Cat("JAVA",90);Animal animal2 = cat;animal2.eat(); }
-
方法傳參
static void eat(Animal A){A.eat(); } public static void main(String[] args) {Dog dog = new Dog("沖沖",18);Cat cat = new Cat("JAVA",90);eat(dog);//這就是傳參實現向上轉型eat(cat); }
-
通過返回值,進行向上轉型
Animal eat(){return new Cat("小貓",2); }
向上轉型的優點:讓代碼實現的更簡單靈活。
向上轉型的缺陷:不能調用到子類的特有方法。
1.4.2向下轉型
將一個子類對象進行向上轉型之后當成父類方法使用,在無法調用子類的方法,但有時候如果需要去調用子類特有的方法,我們就需要用到向下轉型:將父類引用再還原成子類對象即可。
我們再看一段代碼:
class Animal{String name;int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(name+"吃飯....");}
}
class Dog extends Animal{public Dog(String name,int age){super(name,age);}@Overridepublic void eat() {System.out.println(name + "正在吃狗糧.....");}public void bark(){System.out.println(name+"正在旺旺叫.....");}
}class Cat extends Animal{public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃貓糧......");;}public void mew(){System.out.println(name+"正在喵喵叫.....");}
}public class down {public static void main(String[] args) {Cat cat = new Cat("小黑",2);Dog dog = new Dog("小橘", 1);Animal animal1 = cat;animal1.mew();Animal animal2 = dog;animal2.bark();}
}
//最后會編譯失敗,編譯時編譯器將animal當成Animal對象處理
//而Animal類中沒有bark和mew方法,因此編譯失敗
我們可以通過向下轉型操作實現:
//程序可以通過編程,但運行時拋出異常
//現在要強制還原
public class down {public static void main(String[] args) {Cat cat = new Cat("小黑",2);Dog dog = new Dog("小橘", 1);Animal animal1 = cat;//animal1.mew();cat = (Cat)animal1;//進行強制還原cat.mew();Animal animal2 = dog;//animal2.bark();dog = (Dog)animal2;dog.bark();}
}//編譯結果:
//小黑正在喵喵叫.....
//小橘正在旺旺叫.....
因向下轉型用的比較少,而且不安全,萬一轉換失敗,運行時就會拋出異常。Java為了提高向下轉型的安全性,引入了關鍵詞instanceof
,如果表達式為true,則可安全轉換。
public class down {public static void main(String[] args) {Cat cat = new Cat("小黑",2);Dog dog = new Dog("小橘", 1);Animal animal1 = cat;Animal animal2 = dog;//animal1.mew();if(animal1 instanceof Cat){cat = (Cat)animal1;cat.mew();}//animal2.bark();if(animal2 instanceof Dog){dog = (Dog)animal2;dog.bark();}}
}
instanceof
關鍵詞官方介紹:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.20.2
1.5 多態的優缺點
1.5.1 使用多態的好處
- 能夠降低代碼的“圈復雜度”,避免使用大量的if-else
什么叫“圈復雜度”?
圈復雜度是一種描述一段代碼復雜程度的方式。一段代碼如果是平鋪直敘,那么就比較簡單容易理解,但如果有很多的條件分支或循環語句,則認為理解起來更復雜。
因此我們可以簡單粗暴的計算一段代碼中條件語句和循環語句出現的個數,結果個數就稱為“圈復雜度”。如果一個方法的圈復雜度臺稿,就需要考慮重構。
- 可擴展能力更強
如果要增加一種新的功能,使用多態方式的代碼改動成本也比較低。
1.5.2 使用多態的缺陷
代碼的運行效率降低
- 屬性沒有多態性
當父類和子類都有同名屬性的時候,通過父類引用,只能引用父類自己的成員屬性 - 構造方法沒有多態性
盡量不要在構造器中調用方法,如果該方法被子類重寫,就會觸發動態綁定,而此時子類對象還沒構造完成,可能會出現一些隱藏而又極難發現的問題。
結語
好了那么以上就是本篇“【Java】面向對象之多態超級詳解!!”博客的全部內容啦,感謝各位的閱讀=v=,如有不足之處歡迎在評論區指出哦!!
覺得不錯的話別忘了點贊三連支持一下歐!拜托啦這對我真的很重要o(>ω< )o!!!