Java多態對象的類型轉換
這里所說的對象類型轉換,是指存在繼承關系的對象,不是任意類型的對象。當對不存在繼承關系的對象進行強制類型轉換時,java 運行時將拋出 java.lang.ClassCastException 異常。
在繼承鏈中,我們將子類向父類轉換稱為“向上轉型”,將父類向子類轉換稱為“向下轉型”。
很多時候,我們會將變量定義為父類的類型,卻引用子類的對象,這個過程就是向上轉型。程序運行時通過動態綁定來實現對子類方法的調用,也就是多態性。
然而有些時候為了完成某些父類沒有的功能,我們需要將向上轉型后的子類對象再轉成子類,調用子類的方法,這就是向下轉型。
注意:不能直接將父類的對象強制轉換為子類類型,只能將向上轉型后的子類對象再次轉換為子類類型。也就是說,子類對象必須向上轉型后,才能再向下轉型。請看下面的代碼:
public class Demo { public static void main(String args[]) { SuperClass superObj = new SuperClass(); SonClass sonObj = new SonClass(); // 下面的代碼運行時會拋出異常,不能將父類對象直接轉換為子類類型 // SonClass sonObj2 = (SonClass)superObj; // 先向上轉型,再向下轉型 superObj = sonObj; SonClass sonObj1 = (SonClass)superObj; }}class SuperClass{ }class SonClass extends SuperClass{ }
將第7行的注釋去掉,運行時會拋出異常,但是編譯可以通過。
因為向下轉型存在風險,所以在接收到父類的一個引用時,請務必使用 instanceof 運算符來判斷該對象是否是你所要的子類,請看下面的代碼:
public class Demo { public static void main(String args[]) { SuperClass superObj = new SuperClass(); SonClass sonObj = new SonClass(); // superObj 不是 SonClass 類的實例 if(superObj instanceof SonClass){ SonClass sonObj1 = (SonClass)superObj; }else{ System.out.println("①不能轉換"); } superObj = sonObj; // superObj 是 SonClass 類的實例 if(superObj instanceof SonClass){ SonClass sonObj2 = (SonClass)superObj; }else{ System.out.println("②不能轉換"); } }}class SuperClass{ }class SonClass extends SuperClass{ }
運行結果:
①不能轉換
總結:對象的類型轉換在程序運行時檢查,向上轉型會自動進行,向下轉型的對象必須是當前引用類型的子類。
Java多態和動態綁定
在Java中,父類的變量可以引用父類的實例,也可以引用子類的實例。
請讀者先看一段代碼:
public class Demo { public static void main(String[] args){ Animal obj = new Animal(); obj.cry(); obj = new Cat(); obj.cry(); obj = new Dog(); obj.cry(); }}class Animal{ // 動物的叫聲 public void cry(){ System.out.println("不知道怎么叫"); } }class Cat extends Animal{ // 貓的叫聲 public void cry(){ System.out.println("喵喵~"); }}class Dog extends Animal{ // 狗的叫聲 public void cry(){ System.out.println("汪汪~"); }}
運行結果:
不知道怎么叫喵喵~汪汪~
上面的代碼,定義了三個類,分別是 Animal、Cat 和 Dog,Cat 和 Dog 類都繼承自 Animal 類。obj 變量的類型為 Animal,它既可以指向 Animal 類的實例,也可以指向 Cat 和 Dog 類的實例,這是正確的。也就是說,父類的變量可以引用父類的實例,也可以引用子類的實例。注意反過來是錯誤的,因為所有的貓都是動物,但不是所有的動物都是貓。
可以看出,obj 既可以是人類,也可以是貓、狗,它有不同的表現形式,這就被稱為多態。多態是指一個事物有不同的表現形式或形態。
再比如“人類”,也有很多不同的表達或實現,TA 可以是司機、教師、醫生等,你憎恨自己的時候會說“下輩子重新做人”,那么你下輩子成為司機、教師、醫生都可以,我們就說“人類”具備了多態性。
多態存在的三個必要條件:要有繼承、要有重寫、父類變量引用子類對象。
當使用多態方式調用方法時:
首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,則檢查子類是否覆蓋了該方法。
如果子類覆蓋了該方法,就調用子類的方法,否則調用父類方法。
從上面的例子可以看出,多態的一個好處是:當子類比較多時,也不需要定義多個變量,可以只定義一個父類類型的變量來引用不同子類的實例。請再看下面的一個例子:
public class Demo { public static void main(String[] args){ // 借助多態,主人可以給很多動物喂食 Master ma = new Master(); ma.feed(new Animal(), new Food()); ma.feed(new Cat(), new Fish()); ma.feed(new Dog(), new Bone()); }}// Animal類及其子類class Animal{ public void eat(Food f){ System.out.println("我是一個小動物,正在吃" + f.getFood()); }}class Cat extends Animal{ public void eat(Food f){ System.out.println("我是一只小貓咪,正在吃" + f.getFood()); }}class Dog extends Animal{ public void eat(Food f){ System.out.println("我是一只狗狗,正在吃" + f.getFood()); }}// Food及其子類class Food{ public String getFood(){ return "事物"; }}class Fish extends Food{ public String getFood(){ return "魚"; }}class Bone extends Food{ public String getFood(){ return "骨頭"; }}// Master類class Master{ public void feed(Animal an, Food f){ an.eat(f); }}
運行結果:
我是一個小動物,正在吃事物我是一只小貓咪,正在吃魚我是一只狗狗,正在吃骨頭
Master 類的 feed 方法有兩個參數,分別是 Animal 類型和 Food 類型,因為是父類,所以可以將子類的實例傳遞給它,這樣 Master 類就不需要多個方法來給不同的動物喂食。
動態綁定
為了理解多態的本質,下面講一下Java調用方法的詳細流程。
1) 編譯器查看對象的聲明類型和方法名。
假設調用 obj.func(param),obj 為 Cat 類的對象。需要注意的是,有可能存在多個名字為func但參數簽名不一樣的方法。例如,可能存在方法 func(int) 和 func(String)。編譯器將會一一列舉所有 Cat 類中名為func的方法和其父類 Animal 中訪問屬性為 public 且名為func的方法。
這樣,編譯器就獲得了所有可能被調用的候選方法列表。
2) 接下來,編澤器將檢查調用方法時提供的參數簽名。
如果在所有名為func的方法中存在一個與提供的參數簽名完全匹配的方法,那么就選擇這個方法。這個過程被稱為重載解析(overloading resolution)。例如,如果調用 func("hello"),編譯器會選擇 func(String),而不是 func(int)。由于自動類型轉換的存在,例如 int 可以轉換為 double,如果沒有找到與調用方法參數簽名相同的方法,就進行類型轉換后再繼續查找,如果最終沒有匹配的類型或者有多個方法與之匹配,那么編譯錯誤。
這樣,編譯器就獲得了需要調用的方法名字和參數簽名。
3) 如果方法的修飾符是private、static、final(static和final將在后續講解),或者是構造方法,那么編譯器將可以準確地知道應該調用哪個方法,我們將這種調用方式 稱為靜態綁定(static binding)。
與此對應的是,調用的方法依賴于對象的實際類型, 并在運行時實現動態綁。例如調用 func("hello"),編澤器將采用動態綁定的方式生成一條調用 func(String) 的指令。
4)當程序運行,并且 主站蜘蛛池模板: 崇州市| 桑植县| 通海县| 肃南| 喀喇沁旗| 奎屯市| 青神县| 牙克石市| 芜湖市| 克拉玛依市| 偃师市| 塔河县| 政和县| 凤凰县| 浮梁县| 手游| 石家庄市| 鄂温| 广汉市| 正阳县| 鞍山市| 称多县| 丰城市| 托克托县| 张家口市| 凤凰县| 天祝| 大石桥市| 昂仁县| 北票市| 綦江县| 礼泉县| 班玛县| 葵青区| 亚东县| 界首市| 康定县| 河北省| 剑阁县| 大港区| 文水县|