多態的概述
多態是繼封裝、繼承之後,面向對象的第三大特性。 生活中,比如跑的動作,小貓、小狗和大象,跑起來是不一樣的。再比如飛的動作,昆蟲、鳥類和飛機,飛起來也 是不一樣的。可見,同一行爲,通過不同的事物,可以體現出來的不同的形態。多態,描述的就是這樣的狀態。一個對象擁有多種形態,這就是對象的多態性。例如:小明是一個學生(學生形態),但也是一個人(人類形態)。
定義
多態: 是指同一行爲,具有多個不同表現形式。
前提
- 繼承或者實現【二選一】
- 方法的重寫【意義體現:不重寫,無意義】
- 父類引用指向子類對象【格式體現】
多態的體現
多態體現的格式:
- 父類名稱 變量名 = new 子類對象;
或者
- 接口名稱 對象名 = new 實現類名稱();
多態中成員變量的訪問特點
訪問成員變量的兩種方式:
- 直接通過對象名稱訪問成員變量名稱:看等號左邊是誰,優先用誰,沒有則向上找。
- 間接通過成員方法訪問成員變量名稱:看該方法屬於誰,優先用誰,沒有則向上找。
多態中的成員方法的訪問特點
當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫 後方法。 口訣:編譯看左邊,運行看右邊。
在多態的代碼當中,成員方法的訪問規則是:看new的是誰,就優先用誰,沒有則向上找。
對比:
- 成員變量:編譯看左邊,運行還看左邊。
- 成員方法:編譯看左邊,運行看右邊。
代碼舉例
定義父類Father
package myDemo02;
public class Father {
//定義成員變量
String name = "張三";
//定義成員方法
public void method() {
System.out.println();
System.out.println("我是父類中的方法");
}
public void methodFather() {
System.out.println("我是父類中獨有的方法,我的名字是" + name);
}
}
定義子類Son
package myDemo02;
public class Son extends Father {
//定義子類成員變量
String name = "李四";
@Override
//重寫父類的成員方法
public void method() {
System.out.println("我重寫了父類中的方法,我的名字是:" +name);
}
}
定義測試類MyMultiTest
package myDemo02;
public class {
public static void main(String[] args) {
/*父類類型 變量名 = new 子類對象;
父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。
*/
//多態寫法
Father multi = new Son();
//調用子父類重名的方法,編譯看父類,實際運行子類中的方法,
multi.method();
//調用父類中獨有的方法:編譯看父類,因爲子類中沒有相同的方法,所以向上查找
multi.methodFather();
//訪問子父類重名成員變量,等號左邊的是誰,優先使用誰的成員變量
String name = multi.name;
System.out.println(name);
}
}
代碼執行後的結果
多態的好處
實際開發的過程中,父類類型作爲方法形式參數,傳遞子類對象給方法,進行方法的調用(多態規定,執行的是子類重寫的方法),更能體現出多態的擴展 性與便利。
引用類型轉換
多態的轉型分爲向上轉型與向下轉型兩種:
向上轉型
向上轉型:多態本身是子類類型向父類類型向上轉換的過程,這個過程是默認的。 當父類引用指向一個子類對象時,便是向上轉型。 向上轉型一定是安全的。
使用格式:父類類型 變量名 = new 子類類型();
含義:創建一個子類對象,把它當做父類來看待使用
例如
// 創建了一個dog對象,把它看成是動物,沒有如何問題
Animal animal = new Dog();
向下轉型
向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。 一個已經向上轉型的子類對象,將父類引用轉爲子類引用,可以使用強制類型轉換的格式,便是向下轉型。
使用格式:子類類型 變量名 = (子類類型) 父類變量名;
含義:將父類對象還原成爲本來的子類對象
例如:
//將動物對象,還原成爲本來的dog對象
Dog dog = (Dog) animal;
注意事項:
必須保證對象創建的時候就dog對象,才能向下轉型爲dog。否則運行時,卻報出了 ClassCastException ,類型轉換異常!
爲什麼要轉型
當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁 有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給我們帶來的一點"小麻煩"。所以,想要調用子 類特有的方法,必須做向下轉型。
向下轉型容易發生的問題
如果創建了A 對象,卻把它向下轉型成爲B 對象,在運行時,會報出了 ClassCastException ,類型轉換異常!爲了避免ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變量做類型的校驗,格式如下:
- 變量名 instanceof 數據類型
- 如果變量屬於該數據類型,返回true
- 如果變量不屬於該數據類型,返回false。
代碼舉例
定義接口Animal
package myDemo04;
//定義接口
public interface Animal {
//定義抽象方法
public abstract void eat();
}
定義實現類Cat
package myDemo04;
public class Cat implements Animal{
@Override
public void eat() {
System.out.println("我喜歡吃魚");
}
public void CatRun(){
System.out.println("我是貓,跑起來沒有聲音");
}
}
定義實現類Dog
package myDemo04;
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("我喜歡吃骨頭");
}
public void DogRun(){
System.out.println("我是狗,跑的好快好快");
}
}
定義測試類
package myDemo04;
public class Test {
public static void main(String[] args) {
//父類引用指向子類對象就是向上轉型:
Animal animal = new Dog();
//調用方法,編譯看左,運行看右
animal.eat();
// Cat cat = (Cat) animal;創建了Dog對象,轉換爲Cat對象,運行會報ClassCastException ,類型轉換異常!
if (animal instanceof Dog) {
//將父類引用轉爲子類引用,便是向下轉型。
Dog dog = (Dog) animal;
//調用子類中特有的方法
dog.DogRun();
}
if (animal instanceof Cat) {
//將父類引用轉爲子類引用,便是向下轉型。
Cat cat = (Cat) animal;
//調用子類中特有的方法
cat.CatRun();
}
}
}
代碼執行後的結果