多態的定義多態的三個必要條件多態的表現形式多態的種類多態的實現方式基于繼承實現的多態基于接口實現的多態經典案例答案分析多層父子調用的優先級
寫的有點饒,但是可以看到幾個重點是引用的變量和該變量的方法在沒運行時我們是不確定它的具體類型的。
那么為什么會有這種情況呢,根據在繼承中的提現我們發現會發生這種情況的就是向下轉型的問題。 即;我們可以確定貓是動物,但是反過來如果別人給你是一只被袋子裝起來的動物(向上轉型)你是無法判斷它到底是貓還是小老虎(向下轉型)不是么。
因此多態出現的情況就很明顯了,它必須有一個前提場景才能發生;
實現多態有三個必要條件:繼承(extent)、重寫(overriding)、向上轉型
通過實例說明
public class Animal { public void fun1(){ System.out.OutputAnimal 的Fun.....Cat 的Fun2...從程序的運行結果中我們發現, a.fun1()首先是運行父類Animal中的fun1().然后再運行子類Cat中的fun2()。
所以對于多態我們可以總結如下:
指向子類的父類引用由于向上轉型了,它只能訪問父類中擁有的方法和屬性,而對于子類中存在而父類中不存在的方法,該引用是不能使用的,盡管是重載該方法。若子類重寫了父類中的某些方法,在調用這些方法的時候,必定是使用子類中定義的這些方法(動態連接、動態調用)。
java中的多態的表現形式一般分為兩種:overriding(重寫)和overloading(重載)
重寫overriding是父類和子類之間多態性的一種表現重載overloading是一個類中多態性的表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們就可以這樣說此方法被子類重寫overriding,子類中的對象使用這個方法的時候,將調用子類中中的定義,此時對子類來說,父類中的方法定義如同被”屏蔽”了一樣(a不能調用fun1(String str)方法)。如果在一個類中定義了多個同名的方法,它們或有不同的參數或有不同的參數類型,則稱為方法的重載overloading。并且overloading的方式是可以改變返回值類型的,但是父類中并沒有這些重載的方法。
兩種實現方式: - 基于繼承 - 基于接口
上面的例子都是基于繼承實現的,當然下面這個也是基于繼承:
public class Wine { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Wine(){ } public String drink(){ return "喝的是 " + getName(); } /** * 重寫toString() */ public String toString(){ return null; }}public class JNC extends Wine{ public JNC(){ setName("JNC"); } /** * 重寫父類方法,實現多態 */ public String drink(){ return "喝的是 " + getName(); } /** * 重寫toString() */ public String toString(){ return "Wine : " + getName(); }}public class JGJ extends Wine{ public JGJ(){ setName("JGJ"); } /** * 重寫父類方法,實現多態 */ public String drink(){ return "喝的是 " + getName(); } /** * 重寫toString() */ public String toString(){ return "Wine : " + getName(); }}public class Test { public static void main(String[] args) { //定義父類數組 Wine[] wines = new Wine[2]; //定義兩個子類 JNC jnc = new JNC(); JGJ jgj = new JGJ(); //父類引用子類對象 wines[0] = jnc; wines[1] = jgj; for(int i = 0 ; i < 2 ; i++){ System.out.println(wines[i].toString() + "--" + wines[i].drink()); } System.out.println("-------------------------------"); }}OUTPUT:
Wine : JNC--喝的是 JNCWine : JGJ--喝的是 JGJ-------------------------------在上面的代碼中JNC、JGJ繼承Wine,并且重寫了drink()、toString()方法,程序運行結果是調用子類中方法,輸出JNC、JGJ的名稱,這就是多態的表現。不同的對象可以執行相同的行為,但是他們都需要通過自己的實現方式來執行,這就要得益于向上轉型了。
我們都知道所有的類都繼承自超類Object,toString()方法也是Object中方法,當我們這樣寫時:Object o = new JGJ(); System.out.println(o.toString());所以基于繼承實現的多態可以總結如下:對于引用子類的父類類型,在處理該引用時,它適用于繼承該父類的所有子類,子類對象的不同,對方法的實現也就不同,執行相同動作產生的行為也就不同。
這里就不寫代碼了,接口繼承,執行的方法根據具體的子類型來確定,借口本身是不能直接調用的,所以執行的具體方法根據傳遞的子類型來執行它的實現方法。
這是一個非常變態的多態案例,作者寫得非常詳細:
http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx
public class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } }public class B extends A{ public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } }public class C extends B{}public class D extends B{}public class Test { 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)); }}當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。 (但是如果強制把超類轉換成子類的話,就可以調用子類中新添加而超類沒有的方法了。)
好了,先溫習到這里,言歸正傳!上面的案例實際上涉及方法調用的優先問題
優先級由高到低依次為:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。讓我們來看看它是怎么工作的。
比如④,a2.show(b),a2是一個引用變量,類型為A,則this為a2,b是B的一個實例,于是它到類A里面找show(B obj)方法,沒有找到,于是到A的super(超類)找,而A沒有超類,因此轉到第三優先級this.show((super)O),this仍然是a2,這里O為B,(super)O即(super)B即A,因此它到類A里面找show(A obj)的方法,類A有這個方法,但是由于a2引用的是類B的一個對象,B覆蓋了A的show(A obj)方法,因此最終鎖定到類B的show(A obj),輸出為"B and A”。新聞熱點
疑難解答