程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 詳解Java多態對象的類型轉換與靜態綁定

詳解Java多態對象的類型轉換與靜態綁定

編輯:關於JAVA

詳解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 挪用該辦法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved