作者:程序狗
鏈接:https://www.zhihu.com/question/30082151/answer/120520568
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
鏈接:https://www.zhihu.com/question/30082151/answer/120520568
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
用一句話概括就是:
事物在運行過程中存在不同的狀態。先以教科書般舉例說明,下文再舉一個花木蘭替父從軍的例子幫助大家理解. 多態的存在有
三個前提:
1.要有繼承關系
2.子類要重寫父類的方法
3.父類引用指向子類對,
但是其中又有很多細節需要注意。首先我們定義兩個類,一個父類Animal,一個子類Cat。
父類Animal
子類Cat
測試類Demo_Test1
以上的三段代碼充分體現了多態的三個前提,即:
1、存在繼承關系
Cat類繼承了Animal類
2、子類要重寫父類的方法
子類重寫(override)了父類的兩個成員方法eat(),sleep()。其中eat()是非靜態的,sleep()是靜態的(static)。
3、父類數據類型的引用指向子類對象。
測試類Demo_Test1中 Animal am = new Cat();語句在堆內存中開辟了子類(Cat)的對象,並把棧內存中的父類(Animal)的引用指向了這個Cat對象。
到此,滿足了Java多態的的必要三個前提。
---------------------------------------------------華麗的分割線----------------------------------------------------------------------
如果再深究一點呢,我們可以看看上面測試類的輸出結果,或許對多態會有更深層次的認識。猜一猜上面的結果是什么。
可以看出來
子類Cat重寫了父類Animal的非靜態成員方法am.eat();的輸出結果為:貓吃飯。
子類重寫了父類(Animal)的靜態成員方法am.sleep();的輸出結果為:動物在睡覺
未被子類(Cat)重寫的父類(Animal)方法am.run()輸出結果為:動物在奔跑
那么我們可以根據以上情況總結出多態成員訪問的特點:
成員變量
編譯看左邊(父類),運行看左邊(父類)
成員方法
編譯看左邊(父類),運行看右邊(子類)。動態綁定
靜態方法
編譯看左邊(父類),運行看左邊(父類)。
(靜態和類相關,算不上重寫,所以,訪問還是左邊的)
只有非靜態的成員方法,編譯看左邊,運行看右邊
---------------------------------------------------華麗的分割線----------------------------------------------------------------------
那么多態有什么弊端呢?有的,即多態后不能使用子類特有的屬性和方法。往上面的代碼看,子類Cat有一個特有的屬性String name = "tomCat"; 並且還有一個特有的抓老鼠的方法catchMouse()。但是在測試類(Demo_Test)中,我們嘗試調用子類特有的方法catchMouse()和打印子類特有的成員屬性String name = "tomCat"; 就會報錯。
原因就是多態的弊端,就是:不能使用子類特有的成員屬性和子類特有的成員方法。
--------------------------------------------------華麗的分割線----------------------------------------------------------------------
如果在代碼執行過程中還想使用Cat類中特有的屬性String name和它特有的成員方法catchMouse()了怎么辦呢?那我們就可以把這個父類引用指向了子類對象的家伙am再強制變回Cat類型。這樣am就是Cat類型的引用了,指向的也是Cat對象了,自然也能使用Cat類的一切屬性和一切的成員方法。
很明顯,執行強轉語句Cat ct = (Cat)am;之后,ct就指向最開始在堆內存中創建的那個Cat類型的對象了。這就是多態的魅力吧,雖然它有缺點,但是它確實十分靈活,減少多余對象的創建,不用說為了使用子類的某個方法又去重新再堆內存中開辟一個新的子類對象。以上。。
----------------------------------------------------------分割線------------------------------------------------------------------------
啦啦啦,收到這么多贊.很驚訝,舉個通俗點的例子吧.
花木蘭替父從軍
大家都知道花木蘭替父從軍的例子,花木蘭替父親花弧從軍。那么這時候花木蘭是子類,花弧是父類。花弧有自己的成員屬性年齡,姓名,性別。花木蘭也有這些屬性,但是很明顯二者的屬性完全不一樣。花弧有自己的非靜態成員方法‘騎馬殺敵’,同樣花木蘭也遺傳了父親一樣的方法‘騎馬殺敵’。花弧還有一個靜態方法‘自我介紹’,每個人都可以問花弧姓甚名誰。同時花木蘭還有一個自己特有的非靜態成員方法‘塗脂抹粉’。但是,現在花木蘭替父從軍,女扮男裝。這時候相當於父類的引用(花弧這個名字)指向了子類對象(花木蘭這個人),那么在其他類(其他的人)中訪問子類對象(花木蘭這個人)的成員屬性(姓名,年齡,性別)時,其實看到的都是花木蘭她父親的名字(花弧)、年齡(60歲)、性別(男)。當訪問子類對象(花木蘭這個人)的非靜態成員方法(騎馬打仗)時,其實都是看到花木蘭自己運用十八般武藝在騎馬打仗。當訪問花木蘭的靜態方法時(自我介紹),花木蘭自己都是用她父親的名字信息在向別人作自我介紹。並且這時候花木蘭不能使用自己特有的成員方法‘塗脂抹粉’。 -----多態中的向上轉型
那么終於一將功成萬骨枯,打仗旗開得勝了,花木蘭告別了戰爭生活。有一天,遇到了自己心愛的男人,這時候愛情的力量將父類對象的引用(花弧這個名字)強制轉換為子類對象本來的引用(花木蘭這個名字),那么花木蘭又從新成為了她自己,這時候她完全是她自己了。名字是花木蘭,年齡是28,性別是女,打仗依然那樣生猛女漢子,自我介紹則堂堂正正地告訴別人我叫花木蘭。OMG!終於,終於可以使用自己特有的成員方法‘塗脂抹粉’了。從此,花木蘭完全回到了替父從軍前的那個花木蘭了。並且和自己心愛的男人幸福的過完了一生。 -----多態中的向下轉型
----------------------------------------------------------華麗的分割線---------------------------------------------------------------
大家記得哈,向上轉型向下轉型一定是在多態這個前提下哈,否則強制將女兒變成父親,或者將父親變成女人,就變成東方不敗了,系統此時就會報錯非法類型轉換。哈哈哈哈哈。另外開發中一般是利用多態聲明形式參數,並將創建子類的匿名對象作為實際參數。以上。
1.要有繼承關系
2.子類要重寫父類的方法
3.父類引用指向子類對,
但是其中又有很多細節需要注意。首先我們定義兩個類,一個父類Animal,一個子類Cat。
父類Animal
class Animal { int num = 10; static int age = 20; public void eat() { System.out.println("動物吃飯"); } public static void sleep() { System.out.println("動物在睡覺"); } public void run(){ System.out.println("動物在奔跑"); } }
子類Cat
class Cat extends Animal { int num = 80; static int age = 90; String name = "tomCat"; public void eat() { System.out.println("貓吃飯"); } public static void sleep() { System.out.println("貓在睡覺"); } public void catchMouse() { System.out.println("貓在抓老鼠"); } }
class Demo_Test1 { public static void main(String[] args) { Animal am = new Cat(); am.eat(); am.sleep(); am.run(); //am.catchMouse();這里先注釋掉,等會會說明 //System.out.println(am.name);//這里先注釋,待會說明 System.out.println(am.num); System.out.println(am.age); } }
1、存在繼承關系
Cat類繼承了Animal類
2、子類要重寫父類的方法
子類重寫(override)了父類的兩個成員方法eat(),sleep()。其中eat()是非靜態的,sleep()是靜態的(static)。
3、父類數據類型的引用指向子類對象。
測試類Demo_Test1中 Animal am = new Cat();語句在堆內存中開辟了子類(Cat)的對象,並把棧內存中的父類(Animal)的引用指向了這個Cat對象。
到此,滿足了Java多態的的必要三個前提。
---------------------------------------------------華麗的分割線----------------------------------------------------------------------
如果再深究一點呢,我們可以看看上面測試類的輸出結果,或許對多態會有更深層次的認識。猜一猜上面的結果是什么。

子類Cat重寫了父類Animal的非靜態成員方法am.eat();的輸出結果為:貓吃飯。
子類重寫了父類(Animal)的靜態成員方法am.sleep();的輸出結果為:動物在睡覺
未被子類(Cat)重寫的父類(Animal)方法am.run()輸出結果為:動物在奔跑
System.out.println(am.num);//輸出結果為10 System.out.println(am.age);//輸出結果為20
成員變量
編譯看左邊(父類),運行看左邊(父類)
成員方法
編譯看左邊(父類),運行看右邊(子類)。動態綁定
靜態方法
編譯看左邊(父類),運行看左邊(父類)。
(靜態和類相關,算不上重寫,所以,訪問還是左邊的)
只有非靜態的成員方法,編譯看左邊,運行看右邊
---------------------------------------------------華麗的分割線----------------------------------------------------------------------
那么多態有什么弊端呢?有的,即多態后不能使用子類特有的屬性和方法。往上面的代碼看,子類Cat有一個特有的屬性String name = "tomCat"; 並且還有一個特有的抓老鼠的方法catchMouse()。但是在測試類(Demo_Test)中,我們嘗試調用子類特有的方法catchMouse()和打印子類特有的成員屬性String name = "tomCat"; 就會報錯。
am.catchMouse(); System.out.println(am.name);

--------------------------------------------------華麗的分割線----------------------------------------------------------------------
如果在代碼執行過程中還想使用Cat類中特有的屬性String name和它特有的成員方法catchMouse()了怎么辦呢?那我們就可以把這個父類引用指向了子類對象的家伙am再強制變回Cat類型。這樣am就是Cat類型的引用了,指向的也是Cat對象了,自然也能使用Cat類的一切屬性和一切的成員方法。
class Demo_Test { public static void main(String[] args) { Animal am = new Cat(); am.eat(); am.sleep(); am.run(); // am.catchMouse(); // System.out.println(am.name); System.out.println(am.num); System.out.println(am.age); System.out.println("------------------------------"); Cat ct = (Cat)am; ct.eat(); ct.sleep(); ct.run(); ct.catchMouse(); } }

----------------------------------------------------------分割線------------------------------------------------------------------------
啦啦啦,收到這么多贊.很驚訝,舉個通俗點的例子吧.
花木蘭替父從軍
大家都知道花木蘭替父從軍的例子,花木蘭替父親花弧從軍。那么這時候花木蘭是子類,花弧是父類。花弧有自己的成員屬性年齡,姓名,性別。花木蘭也有這些屬性,但是很明顯二者的屬性完全不一樣。花弧有自己的非靜態成員方法‘騎馬殺敵’,同樣花木蘭也遺傳了父親一樣的方法‘騎馬殺敵’。花弧還有一個靜態方法‘自我介紹’,每個人都可以問花弧姓甚名誰。同時花木蘭還有一個自己特有的非靜態成員方法‘塗脂抹粉’。但是,現在花木蘭替父從軍,女扮男裝。這時候相當於父類的引用(花弧這個名字)指向了子類對象(花木蘭這個人),那么在其他類(其他的人)中訪問子類對象(花木蘭這個人)的成員屬性(姓名,年齡,性別)時,其實看到的都是花木蘭她父親的名字(花弧)、年齡(60歲)、性別(男)。當訪問子類對象(花木蘭這個人)的非靜態成員方法(騎馬打仗)時,其實都是看到花木蘭自己運用十八般武藝在騎馬打仗。當訪問花木蘭的靜態方法時(自我介紹),花木蘭自己都是用她父親的名字信息在向別人作自我介紹。並且這時候花木蘭不能使用自己特有的成員方法‘塗脂抹粉’。 -----多態中的向上轉型
那么終於一將功成萬骨枯,打仗旗開得勝了,花木蘭告別了戰爭生活。有一天,遇到了自己心愛的男人,這時候愛情的力量將父類對象的引用(花弧這個名字)強制轉換為子類對象本來的引用(花木蘭這個名字),那么花木蘭又從新成為了她自己,這時候她完全是她自己了。名字是花木蘭,年齡是28,性別是女,打仗依然那樣生猛女漢子,自我介紹則堂堂正正地告訴別人我叫花木蘭。OMG!終於,終於可以使用自己特有的成員方法‘塗脂抹粉’了。從此,花木蘭完全回到了替父從軍前的那個花木蘭了。並且和自己心愛的男人幸福的過完了一生。 -----多態中的向下轉型
----------------------------------------------------------華麗的分割線---------------------------------------------------------------
大家記得哈,向上轉型向下轉型一定是在多態這個前提下哈,否則強制將女兒變成父親,或者將父親變成女人,就變成東方不敗了,系統此時就會報錯非法類型轉換。哈哈哈哈哈。另外開發中一般是利用多態聲明形式參數,並將創建子類的匿名對象作為實際參數。以上。