1.多態的幾個必要條件
1.有繼承關係
2.有方法的重寫
3.父類引用指向子類對象
2.多態中類成員的變現情況
父類:
class Father{
public int num = 10;
public String print(){
return "Father的print()方法!";
}
public static String staticprint(){
return "Father的staticprint()方法";
}
}
子類:
class Son extends Father{
public int num = 20;
public String print(){
return "Son的print()方法";
}
public static String statocprint(){
return "Son的staticprint()方法";
}
}
測試類:
public class Main {
public static void main(String[] args) {
Father f = new Son(); //父類引用指向子類對象
System.out.println("f的num值是:" + f.num);
System.out.println("f的print()調用輸出是:" + f.print());
System.out.println("f的staticprint()調用輸出是:" + f.staticprint());
}
}
測試類輸出結果:
f的num值是:10
f的print()調用輸出是:Son的print()方法
f的staticprint()調用輸出是:Father的staticprint()方法
這裏面涉及到子類父類中的成員變量,成員方法,與靜態方法的關係,一一進行解釋。
成員變量:
可以看到在成員變量中,如果一個引用是Father類型的,那麼他在指向堆中對象的時候只能看到Father對象中的變量情況(num = 10),看不到Son對象中的變量情況(num = 20),因此輸出f.num的時候輸出的時輸出的就是Father類中的num值。
成員方法:
在成員方法之中,父類引用指向子類對象之後,要訪問成員方法的時候,編譯的時候對象指向的是父類的方法區,但是運行的時候指向的是子類的方法區域。因此輸出結果的時候輸出的是子類的方法結果,但是因爲編譯的時候會指向父類的方法區,因此如果父類中沒有print()這一方法,則程序在編譯過程中就會報錯!
靜態方法:
靜態方法,即可以用類名進行引用的方法,顯而易見的取決於你的引用類型,該程序中引用類型是Father,因此輸出的結果肯定是父類Father中的靜態方法,與Father.staticprint()的輸出結果是一致的。
總而言之,成員變量,成員方法與靜態方法在多態中編譯時與運行時候的關係就是:
(Father f = new Son())
成員變量:編譯看左邊(Father),運行看左邊(Father);
成員方法:編譯看左邊(Father),運行看右邊(Son);
靜態方法:編譯看左邊(Father),運行看左邊(Father);
3.多態中的向上轉型和向下轉型
Father f = new Son(); //父類引用指向子類對象
父類引用指向子類對象就是向上轉型,即將當前的概念提升爲一個更寬泛的概念。
但是現在的f只能使用Father中有的方法,但是如果我們需要使用Son中特有的方法呢?
就需要向下轉型:
Son s = (Son)f;
即將f專爲一個更加具體的類型。
通過了解向下轉型和向上轉型,就會發現多態的一個弊端,即不能直接使用子類特有的方法,必須得向下轉型。
4.多態的好處和弊端
弊端:不能使用子類特有的屬性和行爲。
好處:提高了代碼的可維護性(繼承保證)和可擴展性(多態保證)。
提高維護性是指因爲繼承的特性,使得我們只需要對父類進行修改就可以影響到繼承於它的所有子類。
可擴展性:
public class Main {
public static void main(String[] args) {
method(new Cat());
method(new Dog());
}
public static void method(Cat c){
c.eat();
}
}
class Animal{
public void eat(){
System.out.println("動物喫飯");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("貓喫魚");
}
public void catchmouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗喫肉");
}
public void lookhome(){
System.out.println("看家");
}
}
顯然以上代碼method(new Dog());這個語句是錯誤的,因爲method()方法中傳入的參數是Cat類型的,而這一個句子傳入的參數是Dog類型的,很明顯我們並不能說狗是貓,因此我們如果也想用這種靜態方法的方式來調用Dog中的eat()方法,一種方式就是在重新寫一個method()方法:
public static void method(Dog d){
d.eat();
}
但是這樣看上去就會顯得代碼十分的多於重複,爲了兩個類型定義兩種方法,那如果有很多種類型的對象,我們就需要定義許多十分相似的方法,這顯然是不允許的,因此我們可以使用多態,因爲Dog和Cat的父類都是Animal,因此我們可以只使用以下一個方法即可:
public static void method(Animal a){
a.eat();
}
其實就是這個使用了多態的這兩條語句:
Animal a = new Cat();
Animal a = new Dog();
這顯然是合理的,也是一種更簡潔的做法。這也是多態在實際操作中最經常被運用的情況。