詳解Java多態對象的類型轉換與靜態綁定。本站提示廣大學習愛好者:(詳解Java多態對象的類型轉換與靜態綁定)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解Java多態對象的類型轉換與靜態綁定正文
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)當法式運轉,而且釆用靜態綁定挪用辦法時,JVM必定會挪用與 obj 所援用對象的現實類型最適合的誰人類的辦法。我們曾經假定 obj 的現實類型是 Cat,它是 Animal 的子類,假如 Cat 中界說了 func(String),就挪用它,不然將在 Animal 類及其父類中尋覓。
每次挪用辦法都要停止搜刮,時光開支相當年夜,是以,JVM事後為每一個類創立了一個辦法表(method lable),個中列出了一切辦法的稱號、參數簽名和所屬的類。如許一來,在真正挪用辦法的時刻,虛擬機僅查找這個表就好了。在下面的例子中,JVM 搜刮 Cat 類的辦法表,以便尋覓與挪用 func("hello") 相婚配的辦法。這個辦法既有能夠是 Cat.func(String),也有能夠是 Animal.func(String)。留意,假如挪用super.func("hello"),編譯器將對父類的辦法表迸行搜刮。
假定 Animal 類包括cry()、getName()、getAge() 三個辦法,那末它的辦法表以下:
cry() -> Animal.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
現實上,Animal 也有默許的父類 Object(後續會講授),會繼續 Object 的辦法,所以下面羅列的辦法其實不完全。
假定 Cat 類籠罩了 Animal 類中的 cry() 辦法,而且新增了一個辦法 climbTree(),那末它的參數列表為:
cry() -> Cat.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()
在運轉的時刻,挪用 obj.cry() 辦法的進程以下:
JVM 起首拜訪 obj 的現實類型的辦法表,能夠是 Animal 類的辦法表,也能夠是 Cat 類及其子類的辦法表。
JVM 在辦法表中搜刮與 cry() 婚配的辦法,找到後,就曉得它屬於哪一個類了。
JVM 挪用該辦法。