Facebook 扎克伯格:對元宇宙的投入短期內不會實現盈利
阿新 • • 發佈:2021-10-26
1.什麼是多型?
多型是屬於面向物件的三大特徵之一,他的前提是封裝形成獨立體,獨立體之間存在繼承的關係,從而產生多型機制。
多型是同一個行為具有多個不同表現形式或者說形態的能力。
2.多型就是“同一個行為”發生在“不同的物件上”會產生不同的效果。那麼在java中多型是如何體現的呢?
在java中允許這樣的兩種語法出現:
一種是向上轉型(Upcasting),向上轉型是指子型別轉換為父型別,又被稱為自動型別轉換
一種是向下轉型(Downcasting),向下轉型是指父型別轉換為子型別,又被稱為強制型別轉換。
在java語言中有這樣的一個規定:
無論是向上轉型還是向下轉型,兩種型別之間必須要有繼承關係,沒有繼承關係情況下進行向上轉型或向下轉型的時候編譯器都會報錯,這一點需要死記硬背! 例子: 建立一個動物類public class Animal { public void move(){ System.out.println("動物正在在移動!"); } }
貓類繼承動物類
public class Cat extends Animal{ //方法覆蓋 public void move(){ System.out.println("貓咪正在移動!"); } //特有方法抓老鼠 public void catchMouse(){ System.out.println("貓咪會抓老鼠"); } }
鳥類繼承動物類
public class Bird extends Animal{ @Override public void move() { System.out.println("鳥兒在移動!"); } //特有方法 public void fly(){ System.out.println("鳥兒在飛翔!"); } }
測試:
public class Test { public static void main(String[] args) { Animal a1 = new Cat(); a1.move(); Animal a2 = new Bird(); a2.move(); } }執行結果是: 貓咪正在移動! 鳥兒在移動! 以上程式演示的就是多型,多型就是“同一個行為(move)”作用在“不同的物件上”會有不同的表現結果。 java中之所以有多型機制,是因為java允許一個父型別的引用指向一個子型別的物件。 也就是說允許這種寫法: Animal a2=newBird(), 因為 Bird is a Animal是能夠說通的。 其中 Animal a1=newCat() Animal a2=newBird() 都是父型別引用指向了子型別物件,都屬於向上轉型(Upcasting),或者叫做自動型別轉換。 我來解釋一下這段程式碼片段 Animal a1=newCat(); a1.move(); Java程式包括編譯和執行兩個階段,分析Java程式一定要先分析編譯階段,然後再分析執行階段,在編譯階段編譯器只知道a1變數的資料型別是Animal,那麼此時編譯器會去Animal.class位元組碼中查詢move()方法,發現Animal.class位元組碼中存在move()方法,然後將該move()方法繫結到a1引用上,編譯通過了,這個過程我們可以理解為“靜態繫結”階段完成了。 緊接著程式開始執行,進入執行階段,在執行的時候實際上在堆記憶體中new的物件是Cat型別,也就是說真正在move移動的時候,是Cat貓物件在移動,所以執行的時候就會自動執行Cat類當中的move()方法,這個過程可以稱為“動態繫結”。 但無論是什麼時候,必須先“靜態繫結”成功之後才能進入“動態繫結”階段。
publicclass Test { public static void main(String[] args) { Animal a = new Cat(); a.catchMouse(); } }
有人認為Cat貓是可以抓老鼠的呀,為什麼會編譯報錯呢? 那是因為“Animal a=newCat();”在編譯的時候,編譯器只知道a變數的資料型別是Animal,也就是說它只會去Animal.class位元組碼中查詢catchMouse()方法,結果沒找到,自然“靜態繫結”就失敗了,編譯沒有通過。就像以上描述的錯誤資訊一樣:在型別為Animal的變數a中找不到方法catchMouse()。
public class Test { public static void main(String[] args) { //向上轉型 Animal a = new Cat(); //向下轉型,為了呼叫子類中的特有方法 Cat c = (Cat)a; c.catchMouse(); } }執行結果: 我們可以看到直接使用a引用是無法呼叫catchMouse()方法的 因為這個方法屬於子類Cat中特有的行為,不是所有Animal動物都可以抓老鼠的,要想讓它去抓老鼠,就必須做向下轉型(Downcasting),也就是使用強制型別轉換將Animal型別的a引用轉換成Cat型別的引用c(Cat c = (Cat)a;),使用Cat型別的c引用呼叫catchMouse()方法。 通過這個案例,可以得出:只有在訪問子型別中特有資料的時候,需要先進行向下轉型。向下轉型會有什麼風險嘛? 來看這個例子:
public class Test { public static void main(String[] args) { Animal a = new Bird(); Cat c = (Cat)a; } }以上的異常是很常見的ClassCastException,翻譯為型別轉換異常,這種異常通常出現在向下轉型的操作過程當中,當型別不相容的情況下進行轉型出現的異常,之所以出現此異常是因為在程式執行階段a引用指向的物件是一隻小鳥,然後我們要將一隻小鳥轉換成一隻貓,這顯然是不合理的,因為小鳥和貓之間是沒有繼承關係的。 為了避免這種異常的發生,建議在進行向下轉型之前進行執行期型別判斷,這就需要我們學習一個運算子了,它就是instanceof。 instanceof運算子的語法格式是這樣的: (引用 instanceof 型別) instanceof 運算子的運算結果是布林型別,可能是true,也可能是false, 假設(c instanceof Cat)結果是true則表示在執行階段c引用指向的物件是Cat型別,如果結果是false則表示在執行階段c引用指向的物件不是Cat型別。 有了instanceof運算子,向下轉型就可以這樣寫了:
public class Test { public static void main(String[] args) { Animal a = new Bird(); if(a instanceof Cat) { Cat c = (Cat)a; c.catchMouse(); } } }以上程式執行之後不再發生異常,並且什麼也沒有輸出,那是因為if語句的條件並沒有成立,因為在執行階段a引用指向的物件不是Cat型別,所以(ainstanceofCat)是false,自然就不會進行向下轉型了,也不會出現ClassCastException異常了。在實際開發中,java中有這樣一條預設的規範需要大家記住:在進行任何向下轉型的操作之前,要使用instanceof進行判斷,這是一個很好的程式設計習慣。 總結: 多型存在的三個比必要條件是: 1.繼承 2.方法覆蓋 3.父型別引用指向子型別物件 總結: 多型顯然是離不開方法覆蓋機制的,多型就是因為編譯階段繫結父類當中的方法,程式執行階段自動呼叫子類物件上的方法,如果子類物件上的方法沒有進行重寫,這個時候建立子類物件就沒有意義了,自然多型也就沒有意義了,只有子類將方法重寫之後呼叫到子類物件上的方法產生不同效果時,多型就形成了。 實際上方法覆蓋機制和多型機制是捆綁的,誰也離不開誰,多型離不開方法覆蓋,方法覆蓋離開了多型也就沒有意義了。 靜態方法的“覆蓋是沒有意義的”,靜態方法的執行與物件無關,既然和物件無關,那麼就標識和多型無關。 那麼問題來了,java中多型的作用是什麼呢? 1.降低程式的耦合度,提高程式的擴充套件力 2.能使用多型儘量多使用多型 3.父型別引用指向子型別物件 核心是:面向抽象程式設計,儘量不要面向具體程式設計。 下面寫一個例子: 主人類
public class Master { //Master主人類面對的是一個抽象的pet,不面向具體的寵物 public void feed(Pet pet){ //Pet pet是一個父型別的引用 // 傳進來的可以是Pet pet = new Cat(),Pet pet = new Dog(), //父型別的引用指向了子型別的物件 System.out.println("主人開始餵食了"); pet.eat(); System.out.println("主人餵食完畢了"); } }寵物類:
public class Pet { String name;//寵物有名字 public void eat(){//寵物有吃的方法 } }狗類:繼承寵物類,並重寫eat的方法
public class Dog extends Pet { public Dog(String name){ this.name = name; } public void eat(){ System.out.println(this.name +"吃骨頭!"); } //小狗特有的run方法 public void run(){ System.out.println(this.name + "再跑") } }貓類:繼承寵物類,並重寫eat方法
public class Cat extends Pet { public Cat(String name) { this.name = name; } public void eat(){ System.out.println(this.name + "正在吃魚!"); } }測試類:
public class Test { public static void main(String[] args) { Dog dog = new Dog("小花"); Master master = new Master(); master.feed(dog); Cat cat1 = new Cat("Tom"); master.feed(cat1); Pig pig1 = new Pig("梨花"); master.feed(pig1); } }執行結果: 主人開始餵食了 小花吃骨頭! 主人餵食完畢了 主人開始餵食了 Tom正在吃魚! 主人餵食完畢了 主人開始餵食了 梨花在吃粥! 主人餵食完畢了
本文來自部落格園,作者:程式設計師hg,轉載請註明原文連結:https://www.cnblogs.com/hg-blogs/p/15484749.html