Java的多型
概述
Java的四大基本特性:抽象,封裝,繼承和多型。其中,抽象,封裝,繼承可以說多型的基礎,而多型是封裝,繼承的具體表現。如果非要用專業術語來描述什麼是多型的話
多型是指程式中定義的引用變數所指向具體型別和通過該引用變數發出的方法呼叫在編譯的時候並不確定,而是程式執行期間才確定,就是說一個引用變數到底指向哪一個類的例項物件,該引用變數發出的方法呼叫哪一個類的中的方法,必須在程式執行期間才能確定。
記得大學時老師講多型舉的一個例子:上課鈴響了,同學們都回各自教室上課,這就是多型。這就完了?如果是剛接觸程式設計的同學來說,估計都懵逼了,我們那時就是這種狀態。
多型例項
//上課鈴響了 public class Ring { public void ringSound() { System.out.println("我是鈴聲!!!"); } }
1班的同學聽到鈴聲回去上語文課
public class ClassRoom1 extends Ring { public void ringSound() { System.out.println("classRoom1的同學聽到鈴聲上語文了!!!"); } }
2班的同學聽到鈴聲回去上英語課
public class ClassRoom2 extends Ring { public void ringSound() { System.out.println("classRoom2的同學聽到鈴聲上英語了!!!"); } }
Main類
public class Main { public static void main(String[] args) { Ring ring = new ClassRoom1(); Ring ring1 = new ClassRoom2(); ring.ringSound(); ring1.ringSound(); } }
輸出
classRoom1的同學聽到鈴聲上語文了!!! classRoom2的同學聽到鈴聲上英語了!!!
這就是一個簡單的的多型例子,我們從中不難發現,多型存在的幾個關鍵點,
- 有繼承關係( extends )
- 子類重寫 父類方法(ringSound )
- 父類引用指向子類物件 Ring ring = newClassRoom1()
現在我們改下例子,看出現什麼情況
public class Ring { public static void ringSound() { System.out.println("我是鈴聲!!!"); } }
這時發現ClassRoom1 和 ClassRoom2 都報錯了,那我們也給他們都加上static
public class ClassRoom1 extends Ring { public static void ringSound() { System.out.println("classRoom1的同學聽到鈴聲上語文了!!!"); } }
ClassRoom2類
public class ClassRoom2 extends Ring { public static void ringSound() { System.out.println("classRoom2的同學聽到鈴聲上英語了!!!"); } }
預編譯沒報錯了,那麼輸出應該是剛才的結果,main函式跑起來,輸出
我是鈴聲!!! 我是鈴聲!!!
可以發現,結果並不是我們所想的那樣。我們可以得出一個結論:在Java中static修飾的函式不能被子類重寫。
其實Java中,父類含有一個靜態函式,而且在他的子類也同樣有一個返回型別,函式名,引數列表都相同的靜態函式,子類實際上只是將父類中的該同名函式進行隱藏,而非重寫,他們兩個是完全沒有關係的函式,所以他們的行為並不具有多型性。
注意:就是被final 修飾的父類函式是無法被重寫和private 修飾的父類函式無法被繼承。這是Java的規定,就不在舉例說明
多型分類
為了確定執行多型函式的哪一個,所以有兩種情況:編譯時多型,執行時多型
函式過載都是編譯時多型,根據實際引數的資料型別,個數和順序,Java在編譯時就能夠確定執行過載函式中的哪一個。
函式重寫表現出兩種多型性,當物件引用本類例項時,為編譯時多型,否則為執行時多型。
public class Ring { public void ringSound() { System.out.println("我是鈴聲!!!"); } }
Main類
public class Main { public static void main(String[] args) { ClassRoom2 ring1 = new ClassRoom2();//編譯時多型,執行Ring類的ringSound ring1.ringSound(); //編譯時多型,執行ClassRoom2 類的ringSound Ring ring = new ClassRoom2(); ring.ringSound(); //執行時多型,是執行Ring的 ringSound 還是 ClassRoom2 類的ringSound 只有執行時在確定。 } }
最後我們在看個經典的多型例子
A類
public class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } }
B類
public class B extends A { public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } }
C類
public class C extends B { }
D類
public class D extends B { }
Main類
public class Main { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("1--" + a1.show(b)); System.out.println("2--" + a1.show(c)); System.out.println("3--" + a1.show(d)); System.out.println("4--" + a2.show(b)); System.out.println("5--" + a2.show(c)); System.out.println("6--" + a2.show(d)); System.out.println("7--" + b.show(b)); System.out.println("8--" + b.show(c)); System.out.println("9--" + b.show(d)); } }
輸出
1--A and A 2--A and A 3--A and D 4--B and A 5--B and A 6--A and D 7--B and B 8--B and B 9--A and D
你做對了嗎?
花木蘭替父從軍(https://www.zhihu.com/question/30082151/answer/120520568 )
花木蘭替父親花弧從軍。那麼這時候花木蘭是子類,花弧是父類。花弧有自己的成員屬性年齡,姓名,性別。花木蘭也有這些屬性,但是很明顯二者的屬性完全不一樣。花弧有自己的非靜態成員方法‘騎馬殺敵’,同樣花木蘭也遺傳了父親一樣的方法‘騎馬殺敵’。花弧還有一個靜態方法‘自我介紹’,每個人都可以問花弧姓甚名誰。同時花木蘭還有一個自己特有的非靜態成員方法‘塗脂抹粉’。但是,現在花木蘭替父從軍,女扮男裝。這時候相當於父類的引用(花弧這個名字)指向了子類物件(花木蘭這個人),那麼在其他類(其他的人)中訪問子類物件(花木蘭這個人)的成員屬性(姓名,年齡,性別)時,其實看到的都是花木蘭她父親的名字(花弧)、年齡(60歲)、性別(男)。當訪問子類物件(花木蘭這個人)的非靜態成員方法(騎馬打仗)時,其實都是看到花木蘭自己運用十八般武藝在騎馬打仗。當訪問花木蘭的靜態方法時(自我介紹),花木蘭自己都是用她父親的名字資訊在向別人作自我介紹。並且這時候花木蘭不能使用自己特有的成員方法‘塗脂抹粉’。 -----------多型中的向上造型
那麼終於一將功成萬骨枯,打仗旗開得勝了,花木蘭告別了戰爭生活。有一天,遇到了自己心愛的男人,這時候愛情的力量將父類物件的引用(花弧這個名字)強制轉換為子類物件本來的引用(花木蘭這個名字),那麼花木蘭又從新成為了她自己,這時候她完全是她自己了。名字是花木蘭,年齡是28,性別是女,打仗依然那樣生猛女漢子,自我介紹則堂堂正正地告訴別人我叫花木蘭。OMG!終於,終於可以使用自己特有的成員方法‘塗脂抹粉’了。從此,花木蘭完全回到了替父從軍前的那個花木蘭了。並且和自己心愛的男人幸福的過完了一生。 ------多型中的向下轉型