最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java多態(tài)對(duì)象的類型轉(zhuǎn)換與動(dòng)態(tài)綁定

 更新時(shí)間:2015年09月26日 18:28:57   投稿:goldensun  
這篇文章主要介紹了詳解Java多態(tài)對(duì)象的類型轉(zhuǎn)換與動(dòng)態(tài)綁定,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下

Java多態(tài)對(duì)象的類型轉(zhuǎn)換
這里所說(shuō)的對(duì)象類型轉(zhuǎn)換,是指存在繼承關(guān)系的對(duì)象,不是任意類型的對(duì)象。當(dāng)對(duì)不存在繼承關(guān)系的對(duì)象進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí),java 運(yùn)行時(shí)將拋出 java.lang.ClassCastException 異常。

在繼承鏈中,我們將子類向父類轉(zhuǎn)換稱為“向上轉(zhuǎn)型”,將父類向子類轉(zhuǎn)換稱為“向下轉(zhuǎn)型”。

很多時(shí)候,我們會(huì)將變量定義為父類的類型,卻引用子類的對(duì)象,這個(gè)過(guò)程就是向上轉(zhuǎn)型。程序運(yùn)行時(shí)通過(guò)動(dòng)態(tài)綁定來(lái)實(shí)現(xiàn)對(duì)子類方法的調(diào)用,也就是多態(tài)性。

然而有些時(shí)候?yàn)榱送瓿赡承└割悰](méi)有的功能,我們需要將向上轉(zhuǎn)型后的子類對(duì)象再轉(zhuǎn)成子類,調(diào)用子類的方法,這就是向下轉(zhuǎn)型。

注意:不能直接將父類的對(duì)象強(qiáng)制轉(zhuǎn)換為子類類型,只能將向上轉(zhuǎn)型后的子類對(duì)象再次轉(zhuǎn)換為子類類型。也就是說(shuō),子類對(duì)象必須向上轉(zhuǎn)型后,才能再向下轉(zhuǎn)型。請(qǐng)看下面的代碼:

public class Demo {
  public static void main(String args[]) {
    SuperClass superObj = new SuperClass();
    SonClass sonObj = new SonClass();
    // 下面的代碼運(yùn)行時(shí)會(huì)拋出異常,不能將父類對(duì)象直接轉(zhuǎn)換為子類類型
    // SonClass sonObj2 = (SonClass)superObj;
    // 先向上轉(zhuǎn)型,再向下轉(zhuǎn)型
    superObj = sonObj;
    SonClass sonObj1 = (SonClass)superObj;
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ } 

將第7行的注釋去掉,運(yùn)行時(shí)會(huì)拋出異常,但是編譯可以通過(guò)。

因?yàn)橄蛳罗D(zhuǎn)型存在風(fēng)險(xiǎn),所以在接收到父類的一個(gè)引用時(shí),請(qǐng)務(wù)必使用 instanceof 運(yùn)算符來(lái)判斷該對(duì)象是否是你所要的子類,請(qǐng)看下面的代碼:

public class Demo {
  public static void main(String args[]) {
    SuperClass superObj = new SuperClass();
    SonClass sonObj = new SonClass();
    // superObj 不是 SonClass 類的實(shí)例
    if(superObj instanceof SonClass){
      SonClass sonObj1 = (SonClass)superObj;
    }else{
      System.out.println("①不能轉(zhuǎn)換");
    }
    superObj = sonObj;
    // superObj 是 SonClass 類的實(shí)例
    if(superObj instanceof SonClass){
      SonClass sonObj2 = (SonClass)superObj;
    }else{
      System.out.println("②不能轉(zhuǎn)換");
    }
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ }

運(yùn)行結(jié)果:

①不能轉(zhuǎn)換

總結(jié):對(duì)象的類型轉(zhuǎn)換在程序運(yùn)行時(shí)檢查,向上轉(zhuǎn)型會(huì)自動(dòng)進(jìn)行,向下轉(zhuǎn)型的對(duì)象必須是當(dāng)前引用類型的子類。

Java多態(tài)和動(dòng)態(tài)綁定
在Java中,父類的變量可以引用父類的實(shí)例,也可以引用子類的實(shí)例。

請(qǐng)讀者先看一段代碼:

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{
  // 動(dòng)物的叫聲
  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("汪汪~(yú)");
  }
}

運(yùn)行結(jié)果:

不知道怎么叫
喵喵~
汪汪~(yú)

上面的代碼,定義了三個(gè)類,分別是 Animal、Cat 和 Dog,Cat 和 Dog 類都繼承自 Animal 類。obj 變量的類型為 Animal,它既可以指向 Animal 類的實(shí)例,也可以指向 Cat 和 Dog 類的實(shí)例,這是正確的。也就是說(shuō),父類的變量可以引用父類的實(shí)例,也可以引用子類的實(shí)例。注意反過(guò)來(lái)是錯(cuò)誤的,因?yàn)樗械呢埗际莿?dòng)物,但不是所有的動(dòng)物都是貓。

可以看出,obj 既可以是人類,也可以是貓、狗,它有不同的表現(xiàn)形式,這就被稱為多態(tài)。多態(tài)是指一個(gè)事物有不同的表現(xiàn)形式或形態(tài)。

再比如“人類”,也有很多不同的表達(dá)或?qū)崿F(xiàn),TA 可以是司機(jī)、教師、醫(yī)生等,你憎恨自己的時(shí)候會(huì)說(shuō)“下輩子重新做人”,那么你下輩子成為司機(jī)、教師、醫(yī)生都可以,我們就說(shuō)“人類”具備了多態(tài)性。

多態(tài)存在的三個(gè)必要條件:要有繼承、要有重寫(xiě)、父類變量引用子類對(duì)象。

當(dāng)使用多態(tài)方式調(diào)用方法時(shí):
首先檢查父類中是否有該方法,如果沒(méi)有,則編譯錯(cuò)誤;如果有,則檢查子類是否覆蓋了該方法。
如果子類覆蓋了該方法,就調(diào)用子類的方法,否則調(diào)用父類方法。

從上面的例子可以看出,多態(tài)的一個(gè)好處是:當(dāng)子類比較多時(shí),也不需要定義多個(gè)變量,可以只定義一個(gè)父類類型的變量來(lái)引用不同子類的實(shí)例。請(qǐng)?jiān)倏聪旅娴囊粋€(gè)例子:

public class Demo {
  public static void main(String[] args){
    // 借助多態(tài),主人可以給很多動(dòng)物喂食
    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("我是一個(gè)小動(dòng)物,正在吃" + 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 "魚(yú)";
  }
}
class Bone extends Food{
  public String getFood(){
    return "骨頭";
  }
}
// Master類
class Master{
  public void feed(Animal an, Food f){
    an.eat(f);
  }
}

運(yùn)行結(jié)果:

我是一個(gè)小動(dòng)物,正在吃事物
我是一只小貓咪,正在吃魚(yú)
我是一只狗狗,正在吃骨頭

Master 類的 feed 方法有兩個(gè)參數(shù),分別是 Animal 類型和 Food 類型,因?yàn)槭歉割?,所以可以將子類的?shí)例傳遞給它,這樣 Master 類就不需要多個(gè)方法來(lái)給不同的動(dòng)物喂食。
動(dòng)態(tài)綁定

為了理解多態(tài)的本質(zhì),下面講一下Java調(diào)用方法的詳細(xì)流程。

1) 編譯器查看對(duì)象的聲明類型和方法名。

假設(shè)調(diào)用 obj.func(param),obj 為 Cat 類的對(duì)象。需要注意的是,有可能存在多個(gè)名字為func但參數(shù)簽名不一樣的方法。例如,可能存在方法 func(int) 和 func(String)。編譯器將會(huì)一一列舉所有 Cat 類中名為func的方法和其父類 Animal 中訪問(wèn)屬性為 public 且名為func的方法。

這樣,編譯器就獲得了所有可能被調(diào)用的候選方法列表。

2) 接下來(lái),編澤器將檢查調(diào)用方法時(shí)提供的參數(shù)簽名。

如果在所有名為func的方法中存在一個(gè)與提供的參數(shù)簽名完全匹配的方法,那么就選擇這個(gè)方法。這個(gè)過(guò)程被稱為重載解析(overloading resolution)。例如,如果調(diào)用 func("hello"),編譯器會(huì)選擇 func(String),而不是 func(int)。由于自動(dòng)類型轉(zhuǎn)換的存在,例如 int 可以轉(zhuǎn)換為 double,如果沒(méi)有找到與調(diào)用方法參數(shù)簽名相同的方法,就進(jìn)行類型轉(zhuǎn)換后再繼續(xù)查找,如果最終沒(méi)有匹配的類型或者有多個(gè)方法與之匹配,那么編譯錯(cuò)誤。

這樣,編譯器就獲得了需要調(diào)用的方法名字和參數(shù)簽名。

3) 如果方法的修飾符是private、static、final(static和final將在后續(xù)講解),或者是構(gòu)造方法,那么編譯器將可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)方法,我們將這種調(diào)用方式 稱為靜態(tài)綁定(static binding)。

與此對(duì)應(yīng)的是,調(diào)用的方法依賴于對(duì)象的實(shí)際類型, 并在運(yùn)行時(shí)實(shí)現(xiàn)動(dòng)態(tài)綁。例如調(diào)用 func("hello"),編澤器將采用動(dòng)態(tài)綁定的方式生成一條調(diào)用 func(String) 的指令。

4)當(dāng)程序運(yùn)行,并且釆用動(dòng)態(tài)綁定調(diào)用方法時(shí),JVM一定會(huì)調(diào)用與 obj 所引用對(duì)象的實(shí)際類型最合適的那個(gè)類的方法。我們已經(jīng)假設(shè) obj 的實(shí)際類型是 Cat,它是 Animal 的子類,如果 Cat 中定義了 func(String),就調(diào)用它,否則將在 Animal 類及其父類中尋找。

每次調(diào)用方法都要進(jìn)行搜索,時(shí)間開(kāi)銷相當(dāng)大,因此,JVM預(yù)先為每個(gè)類創(chuàng)建了一個(gè)方法表(method lable),其中列出了所有方法的名稱、參數(shù)簽名和所屬的類。這樣一來(lái),在真正調(diào)用方法的時(shí)候,虛擬機(jī)僅查找這個(gè)表就行了。在上面的例子中,JVM 搜索 Cat 類的方法表,以便尋找與調(diào)用 func("hello") 相匹配的方法。這個(gè)方法既有可能是 Cat.func(String),也有可能是 Animal.func(String)。注意,如果調(diào)用super.func("hello"),編譯器將對(duì)父類的方法表迸行搜索。

假設(shè) Animal 類包含cry()、getName()、getAge() 三個(gè)方法,那么它的方法表如下:
cry() -> Animal.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()

實(shí)際上,Animal 也有默認(rèn)的父類 Object(后續(xù)會(huì)講解),會(huì)繼承 Object 的方法,所以上面列舉的方法并不完整。

假設(shè) Cat 類覆蓋了 Animal 類中的 cry() 方法,并且新增了一個(gè)方法 climbTree(),那么它的參數(shù)列表為:
cry() -> Cat.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()

在運(yùn)行的時(shí)候,調(diào)用 obj.cry() 方法的過(guò)程如下:
JVM 首先訪問(wèn) obj 的實(shí)際類型的方法表,可能是 Animal 類的方法表,也可能是 Cat 類及其子類的方法表。
JVM 在方法表中搜索與 cry() 匹配的方法,找到后,就知道它屬于哪個(gè)類了。
JVM 調(diào)用該方法。

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)本地存儲(chǔ)文件上傳及提供HTTP訪問(wèn)服務(wù)的方法

    SpringBoot實(shí)現(xiàn)本地存儲(chǔ)文件上傳及提供HTTP訪問(wèn)服務(wù)的方法

    這篇文章主要介紹了SpringBoot實(shí)現(xiàn)本地存儲(chǔ)文件上傳及提供HTTP訪問(wèn)服務(wù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Spring之從橋接方法到JVM方法調(diào)用解讀

    Spring之從橋接方法到JVM方法調(diào)用解讀

    這篇文章主要介紹了Spring之從橋接方法到JVM方法調(diào)用解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Java?中的泛型超全詳解

    Java?中的泛型超全詳解

    這篇文章主要介紹了泛型的概念、使用場(chǎng)景、泛型類、泛型方法、泛型接口、類型擦除以及泛型通配符,泛型是一種強(qiáng)大的工具,可以幫助我們編寫(xiě)更安全和更通用的代碼,感興趣的朋友一起看看吧
    2025-03-03
  • java8升級(jí)到j(luò)ava17的兼容性分析與遷移指南

    java8升級(jí)到j(luò)ava17的兼容性分析與遷移指南

    這篇文章主要為大家詳細(xì)介紹了從?Java?8?升級(jí)到?Java?17?的詳細(xì)分析和遷移步驟,包括代碼修改建議,依賴更新和配置調(diào)整,有需要的小伙伴可以參考一下
    2025-04-04
  • springboot如何通過(guò)controller層實(shí)現(xiàn)頁(yè)面切換

    springboot如何通過(guò)controller層實(shí)現(xiàn)頁(yè)面切換

    在Spring Boot中,通過(guò)Controller層實(shí)現(xiàn)頁(yè)面切換背景,Spring Boot的默認(rèn)注解是@RestController,它包含了@Controller和@ResponseBody,@ResponseBody會(huì)將返回值轉(zhuǎn)換為字符串返回,因此無(wú)法實(shí)現(xiàn)頁(yè)面切換,將@RestController換成@Controller
    2024-12-12
  • springcloud gateway如何配置動(dòng)態(tài)路由

    springcloud gateway如何配置動(dòng)態(tài)路由

    本文主要介紹了在SpringCloudGateway中配置動(dòng)態(tài)路由的步驟,包括引入依賴、配置路由源、添加配置中心依賴、配置配置中心、定義路由規(guī)則和刷新配置等內(nèi)容,使路由規(guī)則在配置中心更新時(shí),無(wú)需重啟網(wǎng)關(guān)服務(wù)即可動(dòng)態(tài)應(yīng)用新的路由規(guī)則
    2024-10-10
  • SpringSecurity安全框架的使用

    SpringSecurity安全框架的使用

    SpringSecurity是一個(gè)用于企業(yè)應(yīng)用系統(tǒng)的安全框架,可以控制用戶登錄權(quán)限,實(shí)現(xiàn)不同權(quán)限用戶訪問(wèn)不同內(nèi)容,文章介紹了SpringSecurity的簡(jiǎn)單配置和使用,包括環(huán)境搭建、測(cè)試用例和自定義登錄頁(yè)面的配置
    2025-02-02
  • Mybatis分頁(yè)插件PageHelper的分頁(yè)原理剖析

    Mybatis分頁(yè)插件PageHelper的分頁(yè)原理剖析

    這篇文章主要介紹了Mybatis分頁(yè)插件PageHelper的分頁(yè)原理剖析,PageHelper作為一個(gè)啟動(dòng)器,那么就和其他啟動(dòng)器加載一樣,先讀取spring.factories文件里面配置的類,轉(zhuǎn)成Bean加載本系統(tǒng)中,然后執(zhí)行他的前置后置處理方法,完成初始化,需要的朋友可以參考下
    2023-08-08
  • 多個(gè)java泛型示例分享

    多個(gè)java泛型示例分享

    這篇文章主要介紹了多個(gè)java泛型示例,需要的朋友可以參考下
    2014-04-04
  • spring使用redis操作key-value的示例代碼

    spring使用redis操作key-value的示例代碼

    這篇文章主要介紹了spring使用redis操作key-value的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03

最新評(píng)論

苍南县| 卓资县| 陕西省| 保康县| 鹿邑县| 梨树县| 普安县| 封丘县| 龙海市| 龙江县| 嫩江县| 密山市| 池州市| 永嘉县| 牙克石市| 大埔县| 新泰市| 南漳县| 承德市| 固原市| 内丘县| 确山县| 扶风县| 五指山市| 永丰县| 榆林市| 宁津县| 和田市| 富川| 玉山县| 通城县| 林西县| 永新县| 博爱县| 内江市| 德昌县| 龙游县| 常山县| 乌拉特中旗| 礼泉县| 铁力市|