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

Java中多態(tài)的實(shí)現(xiàn)原理詳細(xì)解析

 更新時(shí)間:2024年01月29日 09:45:22   作者:程序員小玖  
這篇文章主要介紹了Java中多態(tài)的實(shí)現(xiàn)原理詳細(xì)解析,多態(tài)是面向?qū)ο缶幊陶Z言的重要特性,它允許基類的指針或引用指向派生類的對象,而在具體訪問時(shí)實(shí)現(xiàn)方法的動態(tài)綁定,需要的朋友可以參考下

Java多態(tài)概述

多態(tài)是面向?qū)ο缶幊陶Z言的重要特性,它允許基類的指針或引用指向派生類的對象,而在具體訪問時(shí)實(shí)現(xiàn)方法的動態(tài)綁定。

Java 對于方法調(diào)用動態(tài)綁定的實(shí)現(xiàn)主要依賴于方法表,但通過類引用調(diào)用(invokevitual)和接口引用調(diào)用(invokeinterface)的實(shí)現(xiàn)則有所不同。

類引用調(diào)用的大致過程為:Java編譯器將Java源代碼編譯成class文件,在編譯過程中,會根據(jù)靜態(tài)類型將調(diào)用的符號引用寫到class文件中。

在執(zhí)行時(shí),JVM根據(jù)class文件找到調(diào)用方法的符號引用,然后在靜態(tài)類型的方法表中找到偏移量,然后根據(jù)this指針確定對象的實(shí)際類型,使用實(shí)際類型的方法表,偏移量跟靜態(tài)類型中方法表的偏移量一樣,如果在實(shí)際類型的方法表中找到該方法,則直接調(diào)用,否則,認(rèn)為沒有重寫父類該方法。按照繼承關(guān)系從下往上搜索。 

接口引用調(diào)用后面再說吧。

從上圖可以看出,當(dāng)程序運(yùn)行時(shí),需要某個(gè)類時(shí),類載入子系統(tǒng)會將相應(yīng)的class文件載入到JVM中,并在內(nèi)部建立該類的類型信息(這個(gè)類型信息其實(shí)就是class文件在JVM中存儲的一種數(shù)據(jù)結(jié)構(gòu)),包含java類定義的所有信息,包括方法代碼,類變量、成員變量、以及本博文要重點(diǎn)討論的方法表。這個(gè)類型信息就存儲在方法區(qū)。 

注意,這個(gè)方法區(qū)中的類型信息跟在堆中存放的class對象是不同的。

在方法區(qū)中,這個(gè)class的類型信息只有唯一的實(shí)例(所以是各個(gè)線程共享的內(nèi)存區(qū)域),而在堆中可以有多個(gè)該class對象??梢酝ㄟ^堆中的class對象訪問到方法區(qū)中類型信息。就像在java反射機(jī)制那樣,通過class對象可以訪問到該類的所有信息一樣。

重點(diǎn)

方法表是實(shí)現(xiàn)動態(tài)調(diào)用的核心。上面講過方法表存放在方法區(qū)中的類型信息中。為了優(yōu)化對象調(diào)用方法的速度,方法區(qū)的類型信息會增加一個(gè)指針,該指針指向一個(gè)記錄該類方法的方法表,方法表中的每一個(gè)項(xiàng)都是對應(yīng)方法的指針。 這些方法中包括從父類繼承的所有方法以及自身重寫(override)的方法。

拓展

方法區(qū):方法區(qū)和JAVA堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。  運(yùn)行時(shí)常量池:它是方法區(qū)的一部分,Class文件中除了有類的版本、方法、字段等描述信息外,還有一項(xiàng)信息是常量池,用于存放編譯器生成的各種符號引用,這部分信息在類加載時(shí)進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中。  方法區(qū)的內(nèi)存回收目標(biāo)是針對常量池的回收及對類型的卸載。

Java 的方法調(diào)用方式

Java 的方法調(diào)用有兩類,動態(tài)方法調(diào)用與靜態(tài)方法調(diào)用。

靜態(tài)方法調(diào)用是指對于類的靜態(tài)方法的調(diào)用方式,是靜態(tài)綁定的動態(tài)方法調(diào)用需要有方法調(diào)用所作用的對象,是動態(tài)綁定的。

類調(diào)用 (invokestatic) 是在編譯時(shí)就已經(jīng)確定好具體調(diào)用方法的情況。

實(shí)例調(diào)用 (invokevirtual)則是在調(diào)用的時(shí)候才確定具體的調(diào)用方法,這就是動態(tài)綁定,也是多態(tài)要解決的核心問題。

JVM 的方法調(diào)用指令有四個(gè),分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個(gè)是靜態(tài)綁定,后兩個(gè)是動態(tài)綁定的。本文也可以說是對于JVM后兩種調(diào)用實(shí)現(xiàn)的考察。

方法表與方法調(diào)用

如有類定義 Person, Girl, Boy

class Person {
    public String toString() {
        return "I'm a person.";
    }
    public void eat() {
    }
    public void speak() {
    }
}
class Boy extends Person {
    public String toString() {
        return "I'm a boy";
    }
    public void speak() {
    }
    public void fight() {
    }
}
class Girl extends Person {
    public String toString() {
        return "I'm a girl";
    }
    public void speak() {
    }
    public void sing() {
    }
}

當(dāng)這三個(gè)類被載入到 Java 虛擬機(jī)之后,方法區(qū)中就包含了各自的類的信息。Girl 和 Boy 在方法區(qū)中的方法表可表示如下:

可以看到,Girl 和 Boy 的方法表包含繼承自 Object 的方法,繼承自直接父類 Person 的方法及各自新定義的方法。注意方法表?xiàng)l目指向的具體的方法地址,如 Girl 繼承自 Object 的方法中,只有 toString() 指向自己的實(shí)現(xiàn)(Girl 的方法代碼),其余皆指向 Object 的方法代碼;其繼承自于 Person 的方法 eat() 和 speak() 分別指向 Person 的方法實(shí)現(xiàn)和本身的實(shí)現(xiàn)。

如果子類改寫了父類的方法,那么子類和父類的那些同名的方法共享一個(gè)方法表項(xiàng)。

因此,方法表的偏移量總是固定的。所有繼承父類的子類的方法表中,其父類所定義的方法的偏移量也總是一個(gè)定值。 Person 或 Object中的任意一個(gè)方法,在它們的方法表和其子類 Girl 和 Boy 的方法表中的位置 (index) 是一樣的。這樣 JVM 在調(diào)用實(shí)例方法其實(shí)只需要指定調(diào)用方法表中的第幾個(gè)方法即可。

如調(diào)用如下:

class Party {
    void happyHour() {
        Person girl = new Girl();
        girl.speak();
    }
}

當(dāng)編譯 Party 類的時(shí)候,生成 girl.speak()的方法調(diào)用假設(shè)為:

Invokevirtual #12

設(shè)該調(diào)用代碼對應(yīng)著 girl.speak(); #12 是 Party 類的常量池的索引。JVM 執(zhí)行該調(diào)用指令的過程如下所示:

(1)在常量池(這里有個(gè)錯(cuò)誤,上圖為ClassReference常量池而非Party的常量池)中找到方法調(diào)用的符號引用 。

(2)查看Person的方法表,得到speak方法在該方法表的偏移量(假設(shè)為15),這樣就得到該方法的直接引用。 

(3)根據(jù)this指針得到具體的對象(即 girl 所指向的位于堆中的對象)。

(4)根據(jù)對象得到該對象對應(yīng)的方法表,根據(jù)偏移量15查看有無重寫(override)該方法,如果重寫,則可以直接調(diào)用(Girl的方法表的speak項(xiàng)指向自身的方法而非父類);如果沒有重寫,則需要拿到按照繼承關(guān)系從下往上的基類(這里是Person類)的方法表,同樣按照這個(gè)偏移量15查看有無該方法。

接口調(diào)用

因?yàn)?nbsp;Java 類是可以同時(shí)實(shí)現(xiàn)多個(gè)接口的,而當(dāng)用接口引用調(diào)用某個(gè)方法的時(shí)候,情況就有所不同了。

Java 允許一個(gè)類實(shí)現(xiàn)多個(gè)接口,從某種意義上來說相當(dāng)于多繼承,這樣同樣的方法在基類和派生類的方法表的位置就可能不一樣了

interface IDance {
    void dance();
}
class Person {
    public String toString() {
        return "I'm a person.";
    }
    public void eat() {
    }
    public void speak() {
    }
}
class Dancer extends Person implements IDance {
    public String toString() {
        return "I'm a dancer.";
    }
    public void dance() {
    }
}
class Snake implements IDance {
    public String toString() {
        return "A snake.";
    }
    public void dance() {
        //snake dance  
    }
}

可以看到,由于接口的介入,繼承自于接口 IDance 的方法 dance()在類 Dancer 和 Snake 的方法表中的位置已經(jīng)不一樣了,顯然我們無法僅根據(jù)偏移量來進(jìn)行方法的調(diào)用。

Java 對于接口方法的調(diào)用是采用搜索方法表的方式,如,要在Dancer的方法表中找到dance()方法,必須搜索Dancer的整個(gè)方法表。

因?yàn)槊看谓涌谡{(diào)用都要搜索方法表,所以從效率上來說,接口方法的調(diào)用總是慢于類方法的調(diào)用的。

到此這篇關(guān)于Java中多態(tài)的實(shí)現(xiàn)原理詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java多態(tài)的實(shí)現(xiàn)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)求解一元n次多項(xiàng)式的方法示例

    Java實(shí)現(xiàn)求解一元n次多項(xiàng)式的方法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)求解一元n次多項(xiàng)式的方法,涉及java高斯消元法處理矩陣運(yùn)算解多項(xiàng)式的相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • idea的easyCode的 MybatisPlus模板的配置詳解

    idea的easyCode的 MybatisPlus模板的配置詳解

    這篇文章主要介紹了idea的easyCode的 MybatisPlus模板的配置詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java枚舉(Enum)從基礎(chǔ)到高級應(yīng)用詳解

    Java枚舉(Enum)從基礎(chǔ)到高級應(yīng)用詳解

    文章詳細(xì)介紹了Java枚舉(enum)的概念、基礎(chǔ)語法、內(nèi)置方法及高級用法,強(qiáng)調(diào)了枚舉在提高代碼類型安全性、可讀性和維護(hù)性方面的優(yōu)勢,文中還列舉了添加字段、方法和實(shí)現(xiàn)接口等高級用法,并提供了命名規(guī)范、不可變性、異常處理等最佳實(shí)踐建議,需要的朋友可以參考下
    2026-05-05
  • 深入理解Spring中RabbitMQ的Channel

    深入理解Spring中RabbitMQ的Channel

    這篇文章主要介紹了深入理解Spring中RabbitMQ的Channel,在RabbitMq中,channel表示邏輯連接或者叫虛擬連接,是棣屬于TCP連接的,一個(gè)TCP連接里可以創(chuàng)建多個(gè)channel,在Rabbit MQ里,消息的發(fā)送和接收都是基于channel的,需要的朋友可以參考下
    2023-08-08
  • Spring控制Bean加載順序的操作方法

    Spring控制Bean加載順序的操作方法

    正常情況下,Spring 容器加載 Bean 的順序是不確定的,那么我們?nèi)绻枰错樞蚣虞d Bean 時(shí)應(yīng)如何操作?本文將詳細(xì)講述我們?nèi)绾尾拍芸刂?nbsp;Bean 的加載順序,需要的朋友可以參考下
    2024-05-05
  • 關(guān)于IDEA2020.1新建項(xiàng)目maven PKIX 報(bào)錯(cuò)問題解決方法

    關(guān)于IDEA2020.1新建項(xiàng)目maven PKIX 報(bào)錯(cuò)問題解決方法

    這篇文章主要介紹了關(guān)于IDEA2020.1新建項(xiàng)目maven PKIX 報(bào)錯(cuò)問題解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • JCrontab簡單入門實(shí)例詳解

    JCrontab簡單入門實(shí)例詳解

    這篇文章主要為大家詳細(xì)介紹了JCrontab簡單入門實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • SpringBoot配置攔截器實(shí)現(xiàn)過程詳解

    SpringBoot配置攔截器實(shí)現(xiàn)過程詳解

    在系統(tǒng)中經(jīng)常需要在處理用戶請求之前和之后執(zhí)行一些行為,例如檢測用戶的權(quán)限,或者將請求的信息記錄到日志中,即平時(shí)所說的"權(quán)限檢測"及"日志記錄",下面這篇文章主要給大家介紹了關(guān)于在SpringBoot項(xiàng)目中整合攔截器的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • java 中使用匿名類直接new接口詳解及實(shí)例代碼

    java 中使用匿名類直接new接口詳解及實(shí)例代碼

    這篇文章主要介紹了java 中使用匿名類直接new接口詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時(shí)間序列化

    java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時(shí)間序列化

    本篇文章主要介紹了java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時(shí)間序列化,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08

最新評論

舟曲县| 罗平县| 巴中市| 马龙县| 徐闻县| 万年县| 和平县| 葵青区| 繁昌县| 东源县| 迁安市| 休宁县| 梁山县| 永宁县| 澜沧| 长泰县| 即墨市| 措勤县| 伊金霍洛旗| 吉首市| 宽城| 雷波县| 保康县| 双流县| 宿州市| 五大连池市| 镇巴县| 通城县| 文登市| 苏州市| 盐池县| 禹城市| 女性| 庄河市| 大埔县| 伊春市| 合川市| 五大连池市| 湄潭县| 余姚市| 凤翔县|