多型
Java物件導向程式設計有三大特性:封裝,繼承和多型
封裝隱藏類的內部具體實現機制,保護資料,對外界隱藏內部細節,只向外部提供它所允許訪問的方法
繼承是為了複用程式碼和實現向上轉型,當然繼承也是為多型做準備。
多型可以說是Java物件導向的精華所在。
什麼是多型
多型是指允許不同子型別的物件對同一訊息做出不同的響應,簡單來講,就是用同樣的物件呼叫同樣的方法但是卻做了不同的事情。也可以理解為一個事物的多種表現形態。
多型性分為編譯時的多型性和執行時的多型性。例如:
方法過載:實現的是編譯時的多型性
方法重寫:實現的是執行是的多型性
子類物件的多型性,這個是Java多型性中最常用的事。
實現多型的條件
1.方法重寫,也就是說要有繼承吧,只有有了繼承,子類重寫了父類已有的或抽象的方法
2.物件造型,也就是說要有父類的引用指向子類物件
只有這樣,同樣的引用呼叫同樣的方法就會作出不同的響應,簡單來講如下程式碼所示:
父類Person有如下方法:
public void walk(){
System.out.println("人走路");
}
public void eat(){
System.out.println("人吃飯");
}
複製程式碼
如果子類Man類沒有重寫這兩個方法,那麼我們做如下的事情:
Person man = new Man();
man.walk();
複製程式碼
輸出結果:
人走路
複製程式碼
綜上所述:父類引用man指向的是子類物件,這個就是子類物件的多型性(也叫向上轉型),man可以呼叫父類的方法,前提是子類沒有重寫父類方法。
如果子類Man類重寫了父類方法
public void walk(){
System.out.println("男人應該挺拔的走路");
}
public void eat(){
System.out.println("男人應該多吃肉");
}
複製程式碼
同樣的呼叫:
Person man = new Man();
man.walk();
複製程式碼
結果就是:
男人應該挺拔的走路
複製程式碼
綜上所述:子類物件重寫了父類方法,那麼man呼叫父類方法,執行的就是子類的方法。這個也虛擬方法呼叫,說到這裡你應該可以猜到,什麼是多型了吧,就是同樣的父類物件應用,指向不同的子類物件,就會作出不同的響應,例如子類Woman類也重寫了父類的方法:
public void walk(){
System.out.println("女人應該溫柔的走路");
}
public void eat(){
System.out.println("女人應該少吃肉多吃水果蔬菜");
}
複製程式碼
呼叫方法:
Person man = new Man();
Person woman = new Woman();
man.walk();
woman.walk();
複製程式碼
結果為:
男人應該挺拔的走路
女人應該溫柔的走路
複製程式碼
綜上所述:應該是很清晰了什麼是子類物件的多型性,就是你相同的父類引用,不同的子類物件,就會響應對應的子類物件的方法。
方法過載和方法重寫
方法過載和方法重寫兩者都是實現多型的方式,區別在於方法過載實現的是編譯時的多型,方法重寫實現的是執行時的多型,過載發生在同一個類中,同名的方法有不同的引數列表。
注意:方法的過載與返回值無關,簡稱兩同一不同,同一個類,同一個方法,不同的引數列表
方法的重寫發生在子類和父類之間,重寫要求子類方法返回值,方法名,引數列表與父類爆出一致,許可權修飾符大於父類,同時要同為static或同為非static。
注意:構造器不能被繼承,所以不能被重寫,但是卻可以被過載
多型的使用例子
例如在父類Person類中有如下方法:
public void show(Person person){
System.out.println("這是父類show方法");
}
複製程式碼
子類Man類重寫了子類方法:
public void show(Person person){
System.out.println("這是子類show方法");
}
複製程式碼
如果例項化Man物件,呼叫man的show(Person person)方法,就應該傳入Person物件,這個時候就可以傳入父類物件的引用。
Person man = new Man();
man.show(man);
複製程式碼
父類引用指向不同的子類物件,作出不同的響應。
多型經典案例
摘自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));
}
}
複製程式碼
輸出結果為:
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
複製程式碼
結果分析如下:
System.out.println("1--" + a1.show(b)); //a1是A的引用指向是A物件,故呼叫A中的show方法,傳入的是b,b指向的是B物件,因為B繼承與A,故傳入的也可認為是A,結果為A and A
System.out.println("2--" + a1.show(c)); //c指向C物件,C繼承B,B繼承A,相當於傳入A,結果為A and A
System.out.println("3--" + a1.show(d)); //d指向D物件,在A中有傳入D物件的方法,結果為 A and D
System.out.println("4--" + a2.show(b)); //a2是父類引用,指向子類物件,按理說呼叫show方法會執行子類B的show方法,傳入的是b,b指向的是B物件,
//但是B中的show(B)是子類特有的方法,故不能被呼叫,傳入的是B,B繼承與A,相當於傳入A,B類重寫了show(B)方法,結果為B and A
System.out.println("5--" + a2.show(c)); //傳入c,執行B中show(C)的方法,沒有,C繼承B,執行B中show(B),但是這是B中特有方法,不能被呼叫,
//B繼承A,呼叫B中show(A),結果B and A
System.out.println("6--" + a2.show(d)); //傳入D,執行B中show(D)方法,沒有,但是A中有就會執行父類show(D)方法,所以結果為:A and D
System.out.println("7--" + b.show(b)); //b指向B,傳入B, 結果為B and B
System.out.println("8--" + b.show(c)); //傳入c,執行B中show(C),沒有,C繼承B,執行B中show(B),結果為:B and B
System.out.println("9--" + b.show(d)); //傳入d,執行B中show(D)方法,沒有,當時B繼承A,是存在A中show(D)的方法,結果為 A and D
複製程式碼
案例總結
對於a2.show(c)來講,a2是A的應用,故先去A中找show(C)方法(this.show(O)),A中沒有,按理應該去A的父類中找(super.show(O)),但是A沒有父類,故只能在A中找show(c的父類)方法(this.show(super)),c的父類有B和A,A中找到show(A)的方法,但是B重寫了該方法,故執行子類的方法,所以結果為B and A。找的順序是this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。