多態的概念
? ? ? ? 在完成某個行為時,不同的對象在完成時會呈現出不同的狀態。
比如:動物都會吃飯,而貓和狗都是動物,貓在完成吃飯行為時吃貓糧,狗在完成吃飯行為時吃狗糧,貓和狗都會叫,狗在完成這個行為時會狗叫,而哈基米會哈氣。
在編程中,多態是指利用子類繼承父類的特性在使用對象時達到簡化代碼的目的。
多態的實現條件
在java中要實現多態,必須要滿足如下幾個條件,缺一不可:
1. 必須在繼承體系下
2. 子類必須要對父類中方法進行重寫
3. 通過父類的引用調用重寫的方法
多態體現:在代碼運行時,當傳遞不同類對象時,會調用對應類中的方法。
下面是簡單的示例:
public class Animal {public String name;public int age;public Animal(String name,int age) {this.name = name;this.age = age;}public void cry(){System.out.println(name+"正在叫---");}}public class Dog extends Animal{public Dog(String name,int age){super(name,age);}public void cry(){System.out.println(name+"正在狗叫---");}
}ublic class Cat extends Animal{public Cat(String name,int age){super(name,age);}public void cry(){System.out.println(name+"正在哈氣---");}
}public class test {public static void main(String[] args) {Animal dog = new Dog("富貴",3);Animal cat = new Cat("哈吉米",4);dog.cry();cat.cry();}
}
運行結果:
在父類Animal和子類Dog與Cat中都有名為cry的方法,我們可以發現在主方法實例化子類后調用的cry均為子類本身的方法,這種現象就稱之為多態。?
重寫
重寫(override):也稱為覆蓋。重寫是子類對父類非靜態、非private修飾,非?nal修飾,非構造方法等的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!重寫的好處在于子類可以根據需要,定義特定于自己的行為。 也就是說子類能夠根據需要實現父類的方法。
【方法重寫的規則】
1.子類在重寫父類的方法時,一般必須與父類方法原型一致: 返回值類型 方法名 (參數列表) 要完全一致
2.被重寫的方法返回值類型可以不同,但是必須是具有父子關系的
3.訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:如果父類方法被public修飾,則子類中重寫該方法就不能聲明為 protected
4.父類被static、private修飾的方法、構造方法都不能被重寫。
5.重寫的方法, 在編譯器中使用 @Override 注解來顯式指定. 有了這個注解能幫我們進行一些合法性校驗. 例如不小心將方法名字拼寫錯了 (比如寫成 aet), 那么此時編譯器就會發現父類中沒有 aet 方法, 就會編譯報錯, 提示無法構成重寫。
方法的重寫與重載的區別:
在重寫中,兩個方法的參數列表必須相同,返回類型必須相同或者構成父子關系,訪問權限限定符必須相同或者更寬松,而在重載中,參數列表必須不同才能構成重載,返回類型可以隨意修改,訪問權限限定符可以隨意修改。?
靜態綁定:也稱為前期綁定(早綁定),即在編譯時,根據用戶所傳遞實參類型就確定了具體調用那個方法。典型代表函數重載。
動態綁定:也稱為后期綁定(晚綁定),即在編譯時,不能確定方法的行為,需要等到程序運行時,才能夠確定具體調用那個類的方法,典型代表函數重寫。
兩種轉型
向上轉型
概念;創建一個子類對象,將其作為父類對象來使用。
語法格式:父類類型?對象名 = new 子類類型();
?
由于父類包含子類,父類是大范圍,子類是小范圍,所以這種轉型就是一種父類對子類的引用。
?向上轉型的使用場景:
1.直接賦值
Animal dog = new Dog("富貴",3);
將子類直接賦值給父類。?
2.方法傳參
public void AnimalCry(Animal a){a.cry();}
在方法的參數列表中使用父類Animal聲明,在傳參時就可以傳入子類Cat和Dog。?
3.方法返回值
public Animal buyAnimal(int a){if(a==1){return new Cat("貓貓",1);}else if(a==2){return new Dog("狗狗",1);}else{return null;}}
示例:
public class test {public static void animalCry(Animal a){a.cry();}public static Animal buyAnimal(int a){if(a==1){return new Cat("貓貓",1);}else if(a==2){return new Dog("狗狗",1);}else{return null;}}public static void main(String[] args) {Animal dog = new Dog("富貴",3);Animal cat = new Cat("哈吉米",4);animalCry(dog);animalCry(cat);Animal sweet = buyAnimal(1);sweet.cry();sweet = buyAnimal(2);sweet.cry();}
}
在主方法中,我們首先實例化Dog和Cat,然后將兩個子類對象傳參給animalCry,然后用sweet接受buyAnimal返回的子類對象。這里就使用到了三種向上轉型的場景。
向上轉型的優點:使代碼更加方便簡潔。
向上轉型的缺點:不能使用子類特有的方法。
向下轉型
將一個子類對象向上轉型為父類對象后,無法在使用子類特有的方法,此時需要用到向下轉型。
public class Animal {public String name;public int age;public Animal(String name,int age) {this.name = name;this.age = age;}public void cry(){System.out.println(name+"正在叫---");}}public class Dog extends Animal{public Dog(String name,int age){super(name,age);}public void cry(){System.out.println(name+"正在狗叫---");}public void eatBone(){System.out.println(name+"正在啃骨頭---");}
}public class Cat extends Animal{public Cat(String name,int age){super(name,age);}public void cry(){System.out.println(name+"正在哈氣---");}public void eatFish(){System.out.println(name+"正在吃魚---");}
}public class test {public static void main(String[] args) {Dog dog = new Dog("富貴",3);Cat cat = new Cat("哈吉米",4);//向上轉型Animal animal =cat;animal.cry();animal=dog;animal.cry();animal.eatBone();//這里編譯器會將animal作為Animal對象處理//而Animal類中沒有eatBone方法//所以會編譯報錯cat = (Cat)animal;cat.eatFish();//這里對animal進行強制類型轉換為cat//但是animal在向上轉換之前指向的實際是dog//所以這里在運行時會出現異常dog=(Dog)animal;dog.eatBone();}
}
刪除錯誤代碼后的運行結果:
為了確保向下轉型時的安全性,Java引入了instanceof關鍵字
當(a instanceof b )為真時,代表a為b類實例化的對象,以此判斷a的類型。?
多態的優缺點
優點
假設有如下父類和子類:
class Shape {public void draw() {System.out.println("畫圖形!");}}
class Rect extends Shape{public void draw() {System.out.println("?");}
}
class Cycle extends Shape{public void draw() {System.out.println("●");}
}
class Flower extends Shape{public void draw() {System.out.println("?");}
}
如果我們不使用多態思想,那么需要這樣打印這些圖形:
public static void drawShapes() {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle", "rect", "cycle", "rect", "?ower"};for (String shape : shapes) {if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();} else if (shape.equals("?ower")) {flower.draw();}}}
使用多態思想:
public static void drawShapes() {// 我們創建了一個 Shape 對象的數組.Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}}
兩種方法輸出相同,代碼量卻相去甚遠:
我們可以看出多態思想為我們省去了許多if-else分支,降低了代碼的圈復雜度?,這就是多態的第一個優點。
多態的第二個優點時可擴展性強,如果新增一種形狀,使用多態的方法改動也更加簡潔:
class Triangle extends Shape {public void draw() {System.out.println("△");}
}
缺點
1. 屬性沒有多態性
當父類和子類都有同名屬性的時候,通過父類引用,只能引用父類自己的成員屬性
2. 構造方法沒有多態性