淺談Java多態,java多態

來源:互聯網
上載者:User

淺談Java多態,java多態

什麼是Java中的多態?又是一個紙老虎的概念,老套路,把它具體化,細分化,先想三個問題(注意,這裡不是簡單的化整為零,而是要建立在學習一個新概念時的思考架構):

1.這個東西有什麼用?用來幹什麼的?它的意義在哪裡?(顯然,如果是沒用的東西,就沒必要浪費時間了;其實,弄懂了這個問題,就掌握了50%)

2.這個概念或者技能點怎麼用?也就是它的表現形式,如關鍵字、修飾詞、文法什麼的。。。(這個佔25%)

3.這個東西在用的過程中,有哪些關鍵點和細節點?(是的,也佔25%)

上面三個問題搞清楚了,剩下的就是去用了。。。“無他,但手熟爾。”

 

一、第一個問題:多態有什麼用?它存在的意義是什嗎?

多態的作用:使代碼具有松耦合性,滿足開閉原則。(多態,意味著一個對象有著多重特徵,可以在特定的情況下,表現不同的狀態,從而對應著不同的屬性和方法。)WTF?又裝x!松耦合是什麼鬼?還開閉原則?能不能直給?好吧,所謂的“耦合”就是,當你擴充功能的時候,其他與之相應的原始碼也要修改,有點像麻花一樣糾纏不清;“松耦合”是指你雖然擴充了,但不需要修改其他的代碼。這樣就達到了對擴充開放,對修改關閉的效果。

具體怎樣,看個例子就懂了:小強,一個農村熱血青年,來到大城市打工,每天披星戴月,辛勤工作,終於存了些錢。於是小強帶著錢回到老家,請村東頭的媒婆王大娘給自己介紹對象。小強在焦急等待幾天之後,看到王大娘笑眯眯走過來,“強啊,大娘給你找到姑娘了”。。。

故事先講到這兒,我們用代碼來把這件事實現一下:

 1 public class Girl{                //父類--姑娘 2     public int faceScore=60;    //顏值 3     public int love=0; 4     public void say(){        //自我介紹 5         System.out.println("hello world!"); 6     } 7     public void addLove(){        //被求愛後,增加愛心值 8     } 9 }10 public class HubeiGirl extends Girl{        //子類--湖北姑娘11     public int faceScore=70;12     public void say(){13         System.out.println("我叫小紅,我很聰明,也很會做飯。我的愛心值:"+love);14     }15     public void addLove() {                        //重寫增加愛心值方法16         love+=20;17         System.out.println("我的愛心值是:"+love);18     }19     public void cook(){                        //特有方法--做飯20         System.out.println("紅丸子,炸丸子,四喜丸子。。。");21     }22 }23 public class HunanGirl extends Girl{        //子類--湖南姑娘24     public int faceScore=85;25     public void say(){26         System.out.println("我叫小倩,我很可愛,也很會唱歌。我的愛心值:"+love);27     }28     public void addLove() {                        //重寫增加愛心值方法29         love+=10;30         System.out.println("我的愛心值是:"+love);31     }32     public void sing(){                        //特有方法--唱歌33         System.out.println("辣妹子辣~辣妹子辣~~");34     }35 }

代碼很簡單,一個姑娘父類,兩個子類分別是湖北姑娘和湖南姑娘,好,現在小強見了兩個姑娘想向她們分別表達愛意(被求愛後,愛心值會增加),該怎麼實現?代碼如下:

 1 public class XiaoQiang{                            //小強類 2     public void courting(HubeiGirl b){    //向湖北姑娘表達愛意 3         b.addLove(); 4     } 5     public void courting(HunanGirl n){    //向湖南姑娘表達愛意 6         n.addLove(); 7     } 8 } 9 public class Test{10     public static void main(String[] argrs){11         HubeiGirl xiaohong = new HubeiGirl();12         xiaohong.say();13         HunanGirl xiaoqian = new HunanGirl();14         xiaoqian.say();15         XiaoQiang qiang = new XiaoQiang(); 16         qiang.courting(xiaohong);        //小強向湖北姑娘小紅表達愛意17         qiang.courting(xiaoqian);        //小強向湖南姑娘小倩表達愛意    18     } 19 }

這裡先定義了一個小強類,裡面有兩個表達愛意的方法,參數是不同的物件類型,屬於重載。測試類別中建立小紅、小倩和小強對象,運行結果如下:

由結果可以看到,傳入不同的物件類型參數,小強調用不同的表達愛意的方法courting(),要求滿足,bingo!故事繼續,王大娘的婚介事業已經走出中國,在向國際化發展,王大娘認識一個非洲姑娘瑪麗卡(如),要把瑪麗卡介紹給小強。。。這要怎麼實現?如果照著上面來:

 1 public class AfricaGirl extends Girl{        //子類--非洲姑娘 2     public int faceScore=80; 3     public void say(){ 4         System.out.println("我叫瑪麗卡,我很熱情,也很會跳舞。我的愛心值:"+love); 5     } 6     public void addLove() {                        //重寫增加愛心值方法 7         love+=15; 8         System.out.println("我的愛心值是:"+love); 9     }10     public void dance(){                        //特有方法--跳舞11         System.out.println("動起來!gogogogo for it!動起來!");12     }13 }14 public class XiaoQiang{                            //小強類15     public void courting(HubeiGirl b){    //向湖北姑娘表達愛意16         b.addLove();17     }18     public void courting(HunanGirl n){    //向湖南姑娘表達愛意19         n.addLove();20     }21     public void courting(AfricaGirl a){    //向非洲姑娘表達愛意22         a.addLove();23     }24 }

這種做法是先定義子類非洲姑娘,這是必須的,在小強類裡加了一個courting(AfricaGirl a)方法,這樣也能實現要求,但是,這樣有兩個問題:1.增加非洲姑娘類的時候,改動了小強類,不是松耦合,不滿足開閉原則;2.假如王大娘的業務發展的很好,要給小強介紹100個姑娘,難道要改變100次小強類,往裡面加100個方法嗎?

所以,問題來了:要怎樣定義小強類,使得即使不斷增加姑娘也不用改動小強類?這裡就要用到多態了,也是開篇第二個問題的答案。

 

二、第二個問題:多態怎麼用?

其實,就一句話:父類類型的引用指向子類的對象。用多態的思想來定義上面的小強類,如下:

1 public class XiaoQiang{                            //小強類2     public void courting(Girl g){                //參數為父類類型3         g.addLove();4     }5 }

很神奇,可以看到用多態的思想來做,只需要一個參數為父類Girl類型的方法就夠了,先來測試一下:

 1 public class Test { 2     public static void main(String[] args) { 3         HubeiGirl xiaohong = new HubeiGirl(); 4         xiaohong.say(); 5         HunanGirl xiaoqian = new HunanGirl(); 6         xiaoqian.say(); 7         AfricaGirl malika = new AfricaGirl(); 8         malika.say(); 9         XiaoQiang qiang = new XiaoQiang(); 10         qiang.courting(xiaohong);        //小強向湖北姑娘小紅表達愛意11         qiang.courting(xiaoqian);        //小強向湖南姑娘小倩表達愛意12         qiang.courting(malika);            //小強向非洲姑娘瑪麗卡表達愛意13 14     }15 }

功能實現,而且現在無論王大娘給小強介紹多少姑娘都不用修改小強類了。那麼,為什麼把參數從子類類型變成父類類型就能達到如此效果,就是多態呢?

這裡我們先來仔細看一下使用父類作為方法形參實現多態的過程,以“小強向非洲姑娘瑪麗卡表達愛意”為例,qiang.courting(malika)執行的時候,由於對象作為參數時,傳入的只是對象的引用,因此參數部分發生的是:Girl g = malika,也就是將父類類型Girl的引用g指向了子類對象malika(有的說法是,將子類類型的指標賦值給父類類型的指標,一個意思,一般指標是C和C++裡的概念),是的,這就是Java機制允許的父類類型的引用指向子類的對象,就是多態。調用過程是:當我們用一個父類型引用指向子類對象時,會先訪問子類中重寫的父類方法(父類的方法不會再執行),如果子類沒有重寫父類的方法,才會執行父類中的方法。而這種調用方式,就是多態的一種狀態,叫做向上轉型,也是最為容易理解的一種多態方式。具體到上面的例子是,先訪問子類AfricaGirl中的重寫的addLove()方法,有,就不會再去訪問父類Girl中的addLove()方法了。

好,故事繼續,老人家講過:凡是不以結婚為目的的談戀愛都是耍流氓。小強是正經人,於是他要選一個最中意的姑娘,並把這個姑娘的對象返回出來,好讓王大娘進行其它操作。。。這個需求怎麼實現?

 1 public class XiaoQiang{                            //小強類 2     public void courting(Girl g){                //參數為父類類型 3         g.addLove(); 4     } 5  6     public Girl chooseGirl(int num) {        //選擇姑娘的方法,傳回值為Girl類型 7         switch(num) { 8         case 1: 9             HubeiGirl b = new HubeiGirl();    //num為1時,返回湖北姑娘的對象b10             return b;11         case 2:12             HunanGirl n = new HunanGirl();13             return n;14         case 3:15             AfricaGirl a = new AfricaGirl();16             return a;17             default:18                 break;19         }20         return null;21     }22 }

可以看到,小強類中增加了一個選擇姑娘的方法,根據不同的數字返回不同的子類姑娘對象,重點是它的傳回值是父類Girl類型,這就是使用父類作為傳回值實現多態。與上面使用父類作為方法形參實現多態相對,這裡是在傳回值的時候將父類類型的引用指向子類對象。在測試類別Test中調用一下:

1 public class Test {2     public static void main(String[] args) {3         XiaoQiang qiang = new XiaoQiang(); 4         Girl g = qiang.chooseGirl(1);        //傳回值是Girl類型,將傳回值賦給Girl類型的引用g5         qiang.courting(g);                    //表達愛意,增加愛心值6         g.say();                            7     }8 }

可以看到,傳入數字1,返回的是湖北姑娘小紅,既然小紅很會做飯,那我們就調用一下她做飯的方法cook(),如下:

看樣子不太妙,報錯說cook()方法沒有定義,有人可能會問子類HubeiGirl中不是有cook()方法嗎,問題是現在的g是父類Girl類型的引用,而父類Girl中是沒有cook()方法的。這就涉及到向上轉型的一個特點:向上轉型後,父類類型的引用只能調用子類中的重寫的父類方法和父類中的方法,而不能調用子類中特有的方法。這裡cook()是子類HubeiGirl特有的方法,所以不能調用。

娶個老婆居然不會做飯,小強肯定不高興,那該怎麼辦?這裡就需要將返回的父類型傳回值強轉為子類型,並將其賦值給相應子類型的引用。如下:

可以看到,調用cook()成功!,將父類Girl類型的傳回值強轉成子類HubeiGirl類型,再將其賦給子類HubeiGirl類型的引用g,就可以調用子類HubeiGirl中特有的方法了。這種將子類型對象向上轉型成父類型後,再將其轉回子類型的過程就是所謂的向下轉型。(註:不能直接將父類型對象轉型成子類型,一定要之前有向上轉型這個過程才行。)

好,故事講完,小強和小紅從此過上了幸福的生活。

 

三、第三個問題:使用多態的關鍵點和細節?

這個問題在二中已經回答得差不多了,總結一下:

1.繼承:多態是發生在有繼承關係的子類和父類中的。

2.重寫:多態就是多種形態。也就是說,當我們需要實現多態的時候,就需要有父類的方法被子類重寫。否則,如果沒有重寫的方法,就看不出多態的特性,一切按照父類的方法來,還不如不要繼承,直接在父類中添加相應的方法,然後在執行個體化好了。

3.向上轉型:父類類型的引用指向子類的對象。

4.向下轉型:將子類型對象向上轉型成父類型後,再將其轉回子類型的過程。

 5.在Java中有兩種形式可以實現多態,繼承和介面。原理相同,本文只講了繼承情況。

 

四、經典例題

既然學了東西就得用,不然那學東西有什麼用。下面是一道關於多態的經典例題,可以試一下不看答案能不能做出來:

 1 public class A { 2     public String show(D obj) { 3         return ("A and D"); 4     } 5     public String show(A obj) { 6         return ("A and A"); 7     }  8 } 9 10 public class B extends A{11     public String show(B obj){12         return ("B and B");13     }   14     public String show(A obj){15         return ("B and A");16     } 17 }18 19 public class C extends B{20 21 }22 public class D extends B{23 24 }25 26 public class Test {27     public static void main(String[] args) {28         A a1 = new A();29         A a2 = new B();30         B b = new B();31         C c = new C();32         D d = new D();33         34         System.out.println("1--" + a1.show(b));35         System.out.println("2--" + a1.show(c));36         System.out.println("3--" + a1.show(d));37         System.out.println("4--" + a2.show(b));38         System.out.println("5--" + a2.show(c));39         System.out.println("6--" + a2.show(d));40         System.out.println("7--" + b.show(b));41         System.out.println("8--" + b.show(c));42         System.out.println("9--" + b.show(d));      43     }44 }

運行結果如下:

1--A and A2--A and A3--A and D4--B and A5--B and A6--A and D7--B and B8--B and B9--A and D

不知道大家做對了幾個,有沒有像福爾摩斯推理探案一樣的感覺,現在就讓我們戴上獵鹿帽,叼上煙鬥,拿著放大鏡開始吧,首先看看下面這張表示ABCD四個類別關係的圖片:

由可以看到:

1.B類中的show(A obj)是繼承並重寫了A類中的show(A obj)方法;

2.B類中的show(B obj)是重載,B相對A特有的方法;

3.B繼承了A的方法,C和D繼承了B的方法(包含了A的方法)。

現在來一個一個看:

1---- a1.show(b)

子類對象b作為參數,而a1隻能調用show(D obj)和show(A obj)兩個方法,顯然調用show(A obj),典型的多態應用。

2----a1.show(c)

子類的子類對象c作為參數,同樣的,a1隻能show(D obj)和show(A obj)兩個方法,顯然調用show(A obj),這裡表現的是多態中,子類對象不光可以賦給父類的引用,父類以上的引用都可以。而Java裡最終極的父類是Object,所以,Object的引用可以指向任何對象。

3----a1.show(d)

子類的子類對象d作為參數,同樣的,a1隻能show(D obj)和show(A obj)兩個方法,顯然。。。額,裡面有D類型作為參數的方法,所以顯然調用show(D obj)。

4----a2.show(b)

A a2 = new B(); 其中,a2是父類型的引用指向了子類B類型的對象,多態的應用,所以a2能調用的方法有:A類中的show(D obj)、B類中的show(A obj)兩個方法。為什麼不能調用A類中的show(A obj)方法?因為子類B已經繼承並重寫了父類A類中的show(A obj), 所以只會訪問子類B中的show(A obj),不會再去訪問父類A中的show(A obj)。為什麼不能調用B類中的show(B obj)方法?因為此方法是子類B特有的方法,多態中父類A類型的引用a2不能訪問。現在是a2.show(b),參數是子類對象b,顯然調用B類中的show(A obj)方法。

5----a2.show(c)

同上,a2能調用的方法有:A類中的show(D obj)、B類中的show(A obj)兩個方法。B的子類對象c作為實參,顯然調用B類中的show(A obj)方法,類C的父類的父類型作為形參實現多態。

6---a2.show(d)

同上,a2能調用的方法有:A類中的show(D obj)、B類中的show(A obj)兩個方法。B的子類對象d作為實參,裡面有D類型作為形參的方法,所以顯然調用A類中的show(D obj)方法。

7----b.show(b)

B b = new B();這個就是調用B類中的show(B obj)。

8----b.show(c)-------------------它仍然要按照繼承鏈中調用方法的優先順序來確認。

B b = new B();其中,B類繼承了A類的方法,b可以調用的方法有:A類中的show(D obj)、B類中的show(A obj)、B類中的show(B obj)三個方法。B的子類對象c作為實參,調用父類B中的show(B obj),使用父類作為形參實現多態。為什麼不能調用B類中的show(A obj)方法?類A是類C父類的父類,將A類型的引用指向類C的對象c不也是多態允許的?理論上是的,但實際情況是,現在有父類作為形參和父類的父類作為形參實現多態這兩種選擇。這時它就要按照按照繼承鏈中調用方法的優先順序來確認,父類B比父類的父類A優先。其中優先順序為:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

9----b.show(d)

同上,b可以調用的方法有:A類中的show(D obj)、B類中的show(A obj)、B類中的show(B obj)三個方法。。。額,裡面有D類型作為參數的方法,所以顯然調用A類中的show(D obj)。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.