java多型的理解(執行時多型)
說道多型,一定離不開其它兩大特性:封裝和繼承。而多型是在它們的基礎之上表現而來的,息息相關。在記憶中,每一次學習面向物件的時候,都與這三大特性有扯不開的關係,其是面向物件的重點,當然也算是難點。但是,它們就像是一層窗戶紙,只要有一個縫隙,你就完全可以搞懂什麼是面向物件。下面來看看關於多型的一些介紹吧:
是什麼?
文學角度:一千個讀者就有一千零一個哈姆雷特;
公司主管角度:老闆交代下來相同任務,每個人完成的情況不同;
專業角度:同種類的多個物件,在接收到同一個訊息時卻產生了不同反應和效果;
從程式碼形式上看: 父類的物件變數呼叫了子類中重寫的方法(注意: 往往是有一個父類,而他有多個子類,且在這些子類中同時重寫父類的某個方法);
結論:多型的前提是有一個父類,多個子類。
做什麼?
父類的物件變數可以引用本類的物件,也可以引用子類的物件。
由於Object類是超根類(祖先類),因此,它的物件變數可以引用所有類的物件。
特殊運算子: instanceof 屬性
注意: 子類的物件變數可以引用父類的物件嗎?
答案: 不可以
若要進行,則必須強制轉換。
即物件型別的轉換分為以下兩種:
1) 向上轉型: 子類的物件可以直接賦給父類的物件變數。
這種現象也稱之為型別的自動轉換。
2) 向下轉型: 將父類的引用強制轉換成子類的引用。
注意: 它必須強制轉換。
格式: (型別名) 物件變數;
當父類物件變數引用了子類的物件時,則問: 父類的物件變數是否可以直接呼叫子類的特有方法?
答案: 否定的。
例如: Animal 是父類, Dog是子類, 而getName()方法是Dog子類特有的方法。
因此, 當有 Anaimal a = new Dog(); 時,
則 a.getName(); 是否正確?
答: 錯誤。
問: 那如何訪問?
答: 需要先強制轉換(向下轉型)
子類類名 物件變數 = (子類類名)父類的物件變數;
即: Dog d = (Dog)a;
之後, d.getName(); 此時,正確。
什麼情況下需要將物件的引用實現強制轉換(還原)(向下轉型)?
1) 一定是發生多型:
父類的物件變數引用了子類的物件。
Object obj = new Student();
2)一定是想要去訪問(呼叫)子類物件的特有屬性或方法。
父類的物件變數.子類物件的特有方法(屬性); //則錯了。
Stringstr = obj.getSchool(); //錯了。
((子類類名)父類的物件變數).子類物件的特有方法(屬性); //則對了。
Stringstr = ((Student)obj).getSchool(); //對了。
//多型: 父類的物件變數引用了子類的物件。
<span style="font-size:18px;"><span style="font-size:18px;">public class AnimalsTest {
public static void main(String[] args) {
Animal a = new Animal("動物");
Dog d1 = new Dog("狗", "旺財", true, 2);
Cat c1 = new Cat("貓", "豆豆", "blue");
System.out.println( a );
System.out.println( d1 );
System.out.println( c1 );
boolean flag = a instanceof Animal ; //判斷a屬於Animal型別嗎
System.out.println( flag );
flag = a instanceof Dog;
System.out.println( flag );
flag = d1 instanceof Dog;
System.out.println( flag );
flag = d1 instanceof Animal; //當結果為true時,說明它們之間存在繼承關係。
System.out.println( flag );
//多型: 父類的物件變數引用了子類的物件。
Animal a1 = new Dog("狗", "ABC", false, 3);
//System.out.println( a1.getName() ); //呼叫a1的 getName()方法. 此時,它會去Animal類中找 getName()方法。若找不到,則報錯。
//判斷a1是否擁有Dog類的特性,若有,則還原。
if( a1 instanceof Dog ){
Dog dd = (Dog)a1; //將a1強制轉換為Dog型別。
System.out.println( dd.getName() );
}
System.out.println("程式結束。");
}
}
</span></span>
幹什麼?
在呼叫一個方法時,從原始碼上看,無法確定呼叫了哪個物件的方法。只有在程式執行期間根據物件變數引用的實際物件才能確定此方法是
哪個物件的。這種現象稱之為動態繫結 (聯編)。
結論: 動態繫結的前提是:
1) 發生繼承,且一個父類有多個子類。
2) 在每一個子類中對繼承自父類的同一個方法實現了重寫。
3) 發生多型,即: 父類物件變數引用了不同的子類物件。
4) 父類的物件變數呼叫了重寫的方法,即: 發生了動態繫結。從而實現了多的價值。
為什麼?
關於面向物件的三大特性,為了更好地理解我們為什麼要用它,很有必要知道它帶來的好處。當然,站在巨人的肩膀上學習總是高效的,分享一下:
封裝性: 安全性和重用性。
繼承性: 高效性和重用性。
多型性: 統一性(有機性)和高效性。
對於這三大特性的理解不同,可能會有不同的體會,例如:大自然不同的風景呈現,我們享受到的也不同,如果每天都面對著同一幅場景,後果是很可怕的。要想更好的理解,不斷地實踐是很不錯的選擇呢!
業務思想
學習的目的是為了更好地實踐,要想在不斷進行的專案中可以完成的更好、更為的輕鬆,這是離不開多型的。我們可以從最後一個demo中,看出加上動態繫結的圖形選擇,可以很有效的避免我們敲出很多圖形的初始化程式碼,很高效,而且重複的初始化圖形,很容易把我們惹毛的(當然這是開玩笑的),而我們要做的就是,儘量減少我們的工作負擔,更為高效、輕鬆的完成。
多型,你一生都離不開的兩個字,無論在哪裡。
---------------------------------------------------------------------------------------------------------------------------------------------------------------
多型有編譯時多型 和執行時多型。
第一個是通過方法過載實現;第二個是通過方法覆蓋實現(子類覆蓋父類方法)。
第一種就是我們呼叫方法是不用區分引數型別,程式會自動執行相應方法,如: 加法運算,可以使int相加,可以是double相加,都是同一個方法名。
第二種就是動態繫結,使用父類引用指向子類物件,再呼叫某一父類中的方法時,不同子類會表現出不同結果。 這樣的作用就是擴充套件性極好,玩過網遊的話應該知道 遊戲中有不同的角色,它們都有一個父類,它們做相同動作時表現出來的效果就會不一樣,比如跑,魔法師的跑跟戰士的跑就不會一樣,這就是倆者都覆蓋了父類中的跑方法,各自有自己的現實,表現出來多型。 如果有一天你想再加個角色,只用再寫一個類繼承該父類,覆蓋其中的跑方法就行了,其他程式碼不用怎麼改,所以可維護性也很好。
這一個小的程式 或許你體會不到多型的強度作用。其實說到多型就是 面向介面程式設計,它不和具體類盡心掛鉤了
比如 你沒用多型的話 ,
你沒例項化一個物件 就要new一下,那假如你那天改變了需求了呢?那是不是又要改裡面的?這樣不好,所以 你可以通過多型,把需要相似的給提出來,然後繼承它 這樣 以後需要擴充套件你僅僅只是繼承而已。這樣就很簡單。
這個需要多看程式碼 才可以更深刻理解。假設有一個類 叫 鳥類,它擁有屬性翅膀,擁有方法鳴叫,如下
public class Bird{
private Wing wing;
public void moo(){
System.out.println("鳥叫聲");
}
}
鳥類封裝了 翅膀類和moo方法;另外有兩個類都繼承鳥類並重寫了moo方法,分別是鸚鵡和麻雀如下:
鸚鵡類:
public class Parrot extends Bird{
public void moo(){
System.out.println("鸚鵡的叫聲");
}
}
麻雀類:
public class Sparrow extends Bird{
public void moo(){
System.out.println("麻雀的叫聲");
}
}
方法重寫應該懂吧,不懂自己找書看吧;然後你有個妻子她想聽鳥叫,就有個妻子類
public class Wife{
public void listen(Bird bird){
bird.moo();
}
/*這時多型就很好的體現了,你妻子想聽鳥叫,無論什麼鳥都可以給她,但是你想讓她和鸚鵡
*說話,你就買了一隻鸚鵡傳給listen方法,結果你妻子聽到了鸚鵡的叫聲,程式輸出:鸚
*鵡的叫聲
*/
public static void main(String[] args) {
new Wife().listen(new Parrot());
}
}
多型實現了動態繫結,讓程式有了很好的擴充套件性,比如你以後想買一隻燕子送給你妻子,就只需要寫個燕子類Swallow繼承Bird方法就可以了,而不需要再在妻子類裡新增一個方法listen(Swallow swallow)……上面編碼沒用編譯器,可能有錯誤,請諒解