多態
-
多態,事物的多種形態
簡單的理解:同一個行爲具有多個不同表現形式或形態的能力。
書面概述:多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。
生活中案例: 比如你是一個酒神,對酒情有獨鍾。某日回家發現桌上有幾個杯子裏面都裝了白酒,從外面看我們是不可能知道這是些什麼酒,只有喝了之後才能夠猜出來是何種酒。你一喝,這是劍南春、再喝這是五糧液、再喝這是茅臺酒,描述如下
酒 a = new 劍南春(); 酒 b = new 五糧液(); 酒 c = new 茅臺酒();
-
實現多態三個必要條件:
-
父子類
劍南春、五糧液、茅臺酒都是酒的子類,我們只是通過酒這一個父類就能夠引用不同的子類,這就是多態。我們只有在運行的時候纔會知道引用變量所指向的具體實例對象。
class 劍南春 extends 酒 class 五糧液 extends 酒 class 茅臺酒 extends 酒
-
重寫
子類需要重寫父類方法
/** * 酒 * * 帶你輕鬆學Java:恆驪學堂 * www.hliedu.com * QQ羣:107184365 * */ class 酒 { public void wine() { System.out.println("喝酒"); } } class 劍南春 extends 酒{ public void wine() { System.out.println("喝劍南春"); } } class 五糧液 extends 酒{ public void wine() { System.out.println("喝五糧液"); } } class 茅臺酒 extends 酒{ public void wine() { System.out.println("喝茅臺酒"); } }
-
向上轉型
父類 obj = new 子類
我們知道,我們要喝劍南春時,直接實例化劍南春類即可,非常好理解,如下
劍南春 jnc = new 劍南春(); jnc.wine();
但如果我們改爲向上轉型的寫法呢?
酒 jnc = new 劍南春(); jnc.wine();
在這裏我們這樣理解,這裏定義了一個
酒
類型的jnc,它指向劍南春對象實例。由於劍南春
是繼承於酒
,所以劍南春
可以自動向上轉型爲酒
,所以jnc是可以指向劍南春
實例對象的。這樣做存在一個非常大的好處,在繼承中我們知道子類是父類的擴展,它可以提供比父類更加強大的功能,如果我們定義了一個指向子類的父類引用類型,那麼它除了能夠引用父類的共性外,還可以使用子類強大的功能。
-
-
多態表現形式
基於繼承:通常採用抽象類來做,必須要有繼承關係,抽象類中定義抽象方法,子類繼承抽象類後覆寫抽象類中的抽象方法,達到多態效果。多態子類的實例可以賦值給父類的引用
基於接口:指向接口的引用必須是指定這實現了該接口的一個類的實例程序,在運行時,根據對象引用的實際類型來執行對應的方法
-
多態案例
- 使用多態來描述動物類、貓、狗
package com.hliedu.dt2; /** * 多態案例 * * 帶你輕鬆學Java:恆驪學堂 * www.hliedu.com * QQ:3020685261 * */ public class Demo2 { public static void main(String[] args) { Animal animal1 = new Dog(); animal1.eat(); Animal animal2 = new Dog(); animal2.eat(); } } //動物類 class Animal { public void eat() {} } //狗類 class Dog extends Animal { @Override public void eat() { System.out.println("狗喫屎"); } public void lookDoor() { System.out.println("看門"); } } //貓類 class Cat extends Animal { @Override public void eat() { System.out.println("貓喫魚"); } public void playGame() { System.out.println("玩遊戲"); } }
注意:Animal作爲父類,無法訪問子類(Dog,Cat)中自己定義的擴展方法
- 使用多態完成主人喂貓、狗喫東西的功能,要求喂貓和餵狗只能由一個方法完成
package com.hliedu.dt2; /** * 多態案例 * * 帶你輕鬆學Java:恆驪學堂 * www.hliedu.com * QQ:3020685261 * */ public class Demo2 { public static void main(String[] args) { Animal animal1 = new Dog(); animal1.name = "二哈"; Animal animal2 = new Cat(); animal2.name = "二喵"; Master master = new Master(); master.feed(animal1); master.feed(animal2); } } //動物類 class Animal { String name = ""; public void eat() {} } //狗類 class Dog extends Animal { @Override public void eat() { System.out.println("狗喫屎"); } public void lookDoor() { System.out.println("看門"); } } //貓類 class Cat extends Animal { @Override public void eat() { System.out.println("貓喫魚"); } public void playGame() { System.out.println("玩遊戲"); } } class Master { public void feed(Animal animal){ System.out.println("主人開始喂" + animal.name + "喫東西"); animal.eat(); System.out.println(animal.name + "喫完了"); } }
多態內存結構
多態接口形態
向上轉型存在一些缺憾,那就是它必定會導致一些方法和屬性的丟失,而導致我們不能夠獲取它們。所以父類類型的引用可以調用父類中定義的所有屬性和方法,對於只存在與子類中的方法和屬性它就望塵莫及了
package com.hliedu.dt4;
/**
* 多態案例2,接口形態
* 主人餵食
*
* 帶你輕鬆學Java:恆驪學堂
* www.hliedu.com
* QQ:3020685261
*
*/
public class Demo4 {
public static void main(String[] args) {
Animal animal1 = new Dog(10 , "二哈");
Animal animal2 = new Cat(20 , "二喵");
Master master = new Master();
master.feed(animal1);
master.feed(animal2);
}
}
//1:父子關係
//2:方法重寫
//3:向上轉型,父類有一個引用去指向子類
interface Animal {
void eat();
}
class Dog implements Animal{
String name;
int age;
public Dog(int age, String name) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("喫骨頭");
}
public void lookDoor() {
System.out.println(name + "看門");
}
}
class Cat implements Animal {
String name;
public int age;
public Cat(int age, String name) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("喫魚");
}
public void playGame() {
System.out.println(name + "玩遊戲");
}
}
//主人餵食
class Master {
public void feed(Animal animal) {
Dog dog = null;
Cat cat = null;
String name = "";
//instanceof關鍵字
if(animal instanceof Dog) {
dog = (Dog)animal;
name = dog.name;
}else if(animal instanceof Cat) {
cat = (Cat)animal;
name = cat.name;
}
System.out.println("準備喂" + name + "喫東西");
animal.eat();
System.out.println(name + "喫完了");
if(dog != null) {
dog.lookDoor();
}else if(cat != null) {
cat.playGame();
}
}
}
多態繼承鏈調用優先級
在繼承鏈中對象方法的調用存在一個優先級,有優先級順序如下
- this.show(O)
- super.show(O)
- this.show((super)O)
- super.show((super)O)
上述字符描述如下:
- this:當前類對象
- show:執行方法
- O:對象參數
- super:父類
如下代碼輸出什麼?
package com.hliedu.dt5;
/**
* 多態繼承調用鏈
* 帶你輕鬆學Java:恆驪學堂
* www.hliedu.com
* QQ:3020685261
*
*/
public class Demo5 {
public static void main(String[] args) {
Parent p1 = new Parent();
Parent p2 = new ChildA();
ChildA a = new ChildA();
ChildB b = new ChildB();
p1.show(a);//①
p1.show(b);//②
p2.show(a);//③
p2.show(b);//④
a.show(a);//⑤
a.show(b);//⑥
b.show(a);//⑦
b.show(b);//⑧
}
}
class Parent {
public void show(Parent obj) {
System.out.println("Parent中的Parent類型參數");
}
public void show(ChildB obj) {
System.out.println("Parent中的ChildB類型參數");
}
}
class ChildA extends Parent {
public void show(Parent obj) {
System.out.println("ChildA中的Parent類型參數");
}
public void show(ChildA obj) {
System.out.println("ChildA中的ChildA類型參數");
}
}
class ChildB extends ChildA{
}
當超類對象的引用變量 引用子類對象時,被引用對象的類型(而不是引用變量的類型)決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。 (但是如果強制把超類轉換成子類的話,就可以調用子類中新添加而超類沒有的方法了)
課後作業
- 將生活中的某個種類使用多態的方式描寫出來,並且區分出不一樣的行爲
- 舉例至少3個重寫與重載的例子,寫出重載和重寫的各自規則(面試題)