多態
多態是同一個行為具有多個不同表現形式或形態的能力。
多態就是同一個接口,使用不同的實例而執行不同操作。
那么怎么理解這句話呢?
我們假設有基類Animal,兩個Animal的派生類Cat和Dog。
我現在有塊廣告牌,想要輸入什么動物就放什么動物的照片?如果沒有多態,我是不是需要不斷地進行判斷?
那么有了多態,我們可以如下實現:
// 創建Animal類
class Animal{
protected String name; // 可被子類訪問的name
public Animal() {
this.name = "Animal";
}
// 封裝
public String getName() {
return this.name;
}
}
class Cat extends Animal{
Cat(){
name = "Cat";
}
}
class Dog extends Animal{
Dog(){
name = "Dog";
}
}
public class Test {
static public void board(Animal s) {
System.out.println(s.getName());
}
public static void main(String[] args) {
Animal animal = new Animal(); //創建Animal對象
Animal cat = new Cat(); //創建Cat對象
Animal dog = new Dog(); //創建Dog對象
// 三塊廣告牌
board(animal);
board(cat);
board(dog);
}
}
// output
// Animal
// Cat
// Dog
從這個代碼和結果,我們就已經可以看出,多態有什么用了!那么,要怎么實現多態呢!
多態存在的三個必要條件
- 繼承
- 重寫
- 基類引用指向派生類對象(引用還是指向基類)
比如
Parent p = new Child();
當使用多態方式調用方法時,首先檢查基類中是否有該方法,如果沒有,則編譯錯誤;如果有,再去調用派生類的同名方法。
重載(Overload)與重寫(Override)
多態中重寫的基本規則
-
參數必須要一樣,且返回類型必須兼容
重寫是派生類對基類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!
基類定義出其他的程序代碼要如何使用方法。不管基類使用了哪種參數,覆蓋此方法的派生類也一定要使用相同的參數。同樣,不論基類聲明的返回類型是什么,派生類必須要聲明返回一樣的類型或該類型的派生類。要記得,派生類對象必須保證可以執行基類的一切。
-
不能降低方法存取的極限
簡而言之,方法和變量的存取權必須相同或者更為開放。
例如不能把public的方法降低為private。
-
基類的成員方法只能被它的派生類重寫。
-
聲明為 final 的方法不能被重寫。
-
聲明為 static 的方法不能被重寫,但是能夠被再次聲明。
-
構造方法不能被重寫。
-
如果不能繼承一個方法,那么它一定不能被重寫
-
當需要在派生類中調用基類的被重寫方法時,要使用 super 關鍵字。
class Animal{ public void move(){ System.out.println("動物可以移動"); } } class Dog extends Animal{ public void move(){ super.move(); // 應用super類的方法 System.out.println("狗可以跑和走"); } } public class TestDog{ public static void main(String args[]){ Animal b = new Dog(); // Dog 對象 b.move(); //執行 Dog類的方法 } } //編譯結果如下 >>>動物可以移動 >>>狗可以跑和走
重載
重載的意義時同一個類里,兩個方法的名稱相同,但參數不同。
所以,重載和多態沒有半毛錢關系!
重載可以有同一方法的多個不同參數版本以便調用。比如某個方法需要int,調用方就需要把double轉成int然后才能調用。如果有個重載版的方法取用double參數,這樣調用就簡單多了。
因為重載方法不需要滿足定義在基類的多態合約,所以擴展起來比較方便。
重載最常用的地方就是構造器的重載。
原則
-
返回類型可以不同
可以任意改變重載方法的返回類型,只要所有的覆蓋使用不同參數即可。
-
不能只改變返回類型
如果只有返回類型改變但參數一樣,編譯器不會通過編譯。也就是說,被重載的方法必須改變參數列表(參數個數或類型不一樣);
結合前兩條,無法以返回值類型作為重載函數的區分標准。
-
可以更改存儲權限
可以任意地設定重載方法的存儲權限。
-
方法能夠在同一個類中或者在一個派生類中被重載。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下兩個參數類型順序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重寫與重載之間的區別
區別點 | 重載方法 | 重寫方法 |
---|---|---|
參數列表 | 必須修改 | 一定不能修改 |
返回類型 | 可以修改 | 一定不能修改 |
異常 | 可以修改 | 可以減少或刪除,一定不能拋出新的或者更廣的異常 |
訪問 | 可以修改 | 一定不能做更嚴格的限制(可以降低限制) |
總結
方法的重寫(Overriding)和重載(Overloading)是java多態性的不同表現,重寫是基類與派生類之間多態性的一種表現,重載可以理解成多態的具體表現形式。
- (1)方法重載是一個類中定義了多個方法名相同,而他們的參數的數量不同或數量相同而類型和次序不同,則稱為方法的重載(Overloading)。
- (2)方法重寫是在派生類存在方法與基類的方法的名字相同,而且參數的個數與類型一樣,返回值也一樣的方法,就稱為重寫(Overriding)。
- (3)方法重載是一個類的多態性表現,而方法重寫是派生類與基類的一種多態性表現。
附一張網圖
多態成員訪問的特點
Parent p = new Child();
成員變量
編譯看左邊(基類),運行看左邊(基類);無論如何都是訪問基類的成員變量。
成員方法
編譯看左邊(基類),運行看右邊(派生類),動態綁定。
Static方法
編譯看左邊(基類),運行看左邊(基類)。
只有非靜態的成員方法,編譯看左邊,運行看右邊。
這樣,我們也可以得出多態的局限:
不能使用派生類特有的成員屬性和派生類特有的成員方法。