新聞中心
多態(tài)(Polymorphism)屬于面向?qū)ο笕筇卣髦?,它的前提是封裝形成獨立體,獨立體之間存在繼承關(guān)系,從而產(chǎn)生多態(tài)機制。多態(tài)是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力。

創(chuàng)新互聯(lián)主要從事網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)谷城,10余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
重載式多態(tài),也叫編譯時多態(tài)。也就是說這種多態(tài)再編譯時已經(jīng)確定好了。重載大家都知道,方法名相同而參數(shù)列表不同的一組方法就是重載。在調(diào)用這種重載的方法時,通過傳入不同的參數(shù)最后得到不同的結(jié)果。
但是這里是有歧義的,有的人覺得不應(yīng)該把重載也算作多態(tài)。因為很多人對多態(tài)的理解是:程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時并不確定,而是在程序運行期間才確定,這種情況叫做多態(tài)。 這個定義中描述的就是我們的第二種多態(tài)—重寫式多態(tài)。并且,重載式多態(tài)并不是面向?qū)ο缶幊烫赜械模鄳B(tài)卻是面向?qū)ο笕筇匦灾唬ㄈ绻艺f的不對,記得告訴我。。)。
我覺得大家也沒有必要在定義上去深究這些,我的理解是:同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力就是多態(tài),所以我認(rèn)為重載也是一種多態(tài),如果你不同意這種觀點,我也接受。
重寫式多態(tài),也叫運行時多態(tài)。這種多態(tài)通過動態(tài)綁定(dynamic binding)技術(shù)來實現(xiàn),是指在執(zhí)行期間判斷所引用對象的實際類型,根據(jù)其實際的類型調(diào)用其相應(yīng)的方法。也就是說,只有程序運行起來,你才知道調(diào)用的是哪個子類的方法。 這種多態(tài)通過函數(shù)的重寫以及向上轉(zhuǎn)型來實現(xiàn),我們上面代碼中的例子就是一個完整的重寫式多態(tài)。我們接下來講的所有多態(tài)都是重寫式多態(tài),因為它才是面向?qū)ο缶幊讨姓嬲亩鄳B(tài)。
向上轉(zhuǎn)型
子類引用的對象轉(zhuǎn)換為父類類型稱為向上轉(zhuǎn)型。通俗地說就是是將子類對象轉(zhuǎn)為父類對象。此處父類對象可以是接口。
看一個大家都知道的例子:
實例
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("我吃魚");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("我吃骨頭");
}
public void run(){
System.out.println("我會跑");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat(); //向上轉(zhuǎn)型
animal.eat();
animal = new Dog();
animal.eat();
}
}
//結(jié)果:
//我吃魚
//我吃骨頭
這就是向上轉(zhuǎn)型,Animal animal = new Cat(); 將子類對象 Cat 轉(zhuǎn)化為父類對象 Animal。這個時候 animal 這個引用調(diào)用的方法是子類方法。
轉(zhuǎn)型過程中需要注意的問題
-
向上轉(zhuǎn)型時,子類單獨定義的方法會丟失。比如上面Dog類中定義的run方法,當(dāng)animal引用指向Dog類實例時是訪問不到run方法的,animal.run()會報錯。
-
子類引用不能指向父類對象。Cat c = (Cat)new Animal()這樣是不行的。
向上轉(zhuǎn)型的好處
-
減少重復(fù)代碼,使代碼變得簡潔。
-
提高系統(tǒng)擴展性。
向下轉(zhuǎn)型
與向上轉(zhuǎn)型相對應(yīng)的就是向下轉(zhuǎn)型了。向下轉(zhuǎn)型是把父類對象轉(zhuǎn)為子類對象。(請注意!這里是有坑的。)
案例驅(qū)動
先看一個例子:
//還是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();
//輸出 我吃魚
Dog d = ((Dog) a);
d.eat();
// 報錯 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
// 報錯 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat
為什么第一段代碼不報錯呢?相比你也知道了,因為 a 本身就是 Cat 對象,所以它理所當(dāng)然的可以向下轉(zhuǎn)型為 Cat,也理所當(dāng)然的不能轉(zhuǎn)為 Dog,你見過一條狗突然就變成一只貓這種操蛋現(xiàn)象?
而 a1 為 Animal 對象,它也不能被向下轉(zhuǎn)型為任何子類對象。比如你去考古,發(fā)現(xiàn)了一個新生物,知道它是一種動物,但是你不能直接說,啊,它是貓,或者說它是狗。
向下轉(zhuǎn)型注意事項
-
向下轉(zhuǎn)型的前提是父類對象指向的是子類對象(也就是說,在向下轉(zhuǎn)型之前,它得先向上轉(zhuǎn)型)
-
向下轉(zhuǎn)型只能轉(zhuǎn)型為本類對象(貓是不能變成狗的)。
看一個經(jīng)典案例:
實例
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{
}
class D extends B{
}
public class Demo {
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));
}
}
//結(jié)果:
//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
//能看懂這個結(jié)果么?先自分析一下。
前三個,強行分析,還能看得懂。但是第四個,大概你就傻了吧。為什么不是b and b呢?
這里就要學(xué)點新東西了。
當(dāng)父類對象引用變量引用子類對象時,被引用對象的類型決定了調(diào)用誰的成員方法,引用變量類型決定可調(diào)用的方法。如果子類中沒有覆蓋該方法,那么會去父類中尋找。
可能讀起來比較拗口,我們先來看一個簡單的例子:
實例
class X {
public void show(Y y){
System.out.println("x and y");
}
public void show(){
System.out.println("only x");
}
}
class Y extends X {
public void show(Y y){
System.out.println("y and y");
}
public void show(int i){
}
}
class main{
public static void main(String[] args) {
X x = new Y();
x.show(new Y());
x.show();
}
}
//結(jié)果
//y and y
//only x
Y 繼承了 X,覆蓋了 X 中的 show(Y y) 方法,但是沒有覆蓋 show() 方法。
這個時候,引用類型為X的 x 指向的對象為 Y,這個時候,調(diào)用的方法由 Y 決定,會先從 Y 中尋找。執(zhí)行 x.show(new Y());,該方法在 Y 中定義了,所以執(zhí)行的是 Y 里面的方法;
但是執(zhí)行 x.show(); 的時候,有的人會說,Y 中沒有這個方法?。克孟袷侨ジ割愔姓以摲椒?,因為調(diào)用了 X 中的方法。
事實上,Y 類中是有 show() 方法的,這個方法繼承自 X,只不過沒有覆蓋該方法,所以沒有在 Y 中明確寫出來而已,看起來像是調(diào)用了 X 中的方法,實際上調(diào)用的還是 Y 中的。
這個時候再看上面那句難理解的話就不難理解了吧。X是引用變量類型,它決定哪些方法可以調(diào)用;show()和 show(Y y) 可以調(diào)用,而 show(int i)不可以調(diào)用。Y 是被引用對象的類型,它決定了調(diào)用誰的方法:調(diào)用 y 的方法。
上面的是一個簡單的知識,它還不足以讓我們理解那個復(fù)雜的例子。我們再來看這樣一個知識:
繼承鏈中對象方法的調(diào)用的優(yōu)先級:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
如果你能理解這個調(diào)用關(guān)系,那么多態(tài)你就掌握了。我們回到那個復(fù)雜的例子:
abcd 的關(guān)系是這樣的:C/D —> B —> A
我們先來分析4 : a2.show(b)
首先,a2是類型為A的引用類型,它指向類型為B的對象。A確定可調(diào)用的方法:show(D obj)和show(A obj)。
a2.show(b) ==> this.show(b),這里this指的是B。然后.在B類中找show(B obj),找到了,可惜沒用,因為show(B obj)方法不在可調(diào)用范圍內(nèi),this.show(O)失敗,進入下一級別:super.show(O),super指的是A。在A 中尋找show(B obj),失敗,因為沒用定義這個方法。進入第三級別:this.show((super)O),this指的是B。在B中找show((A)O),找到了:show(A obj),選擇調(diào)用該方法。
輸出:B and A
如果你能看懂這個過程,并且能分析出其他的情況,那你就真的掌握了。
我們再來看一下9:b.show(d)
首先,b為類型為B的引用對象,指向類型為B的對象。沒有涉及向上轉(zhuǎn)型,只會調(diào)用本類中的方法。
在B中尋找show(D obj),方法?,F(xiàn)在你不會說沒找到了吧?找到了,直接調(diào)用該方法。
輸出 A and D。
總結(jié)
本篇文章的內(nèi)容大體上就是這些了。我們來總結(jié)一下。
-
多態(tài),簡而言之就是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力。
-
多態(tài)的分類:運行時多態(tài)和編譯時多態(tài)。
-
運行時多態(tài)的前提:繼承(實現(xiàn)),重寫,向上轉(zhuǎn)型
-
向上轉(zhuǎn)型與向下轉(zhuǎn)型。
-
繼承鏈中對象方法的調(diào)用的優(yōu)先級:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
分享標(biāo)題:講解一下Java中的多態(tài)
文章路徑:http://fisionsoft.com.cn/article/ccesieh.html


咨詢
建站咨詢
