欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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(yǔ)言的重要特性,它允許基類(lèi)的指針或引用指向派生類(lèi)的對(duì)象,而在具體訪問(wèn)時(shí)實(shí)現(xiàn)方法的動(dòng)態(tài)綁定,需要的朋友可以參考下

Java多態(tài)概述

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

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

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

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

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

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

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

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

重點(diǎn)

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

拓展

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

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

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

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

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

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

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

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

如有類(lèi)定義 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è)類(lèi)被載入到 Java 虛擬機(jī)之后,方法區(qū)中就包含了各自的類(lèi)的信息。Girl 和 Boy 在方法區(qū)中的方法表可表示如下:

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

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

因此,方法表的偏移量總是固定的。所有繼承父類(lèi)的子類(lèi)的方法表中,其父類(lèi)所定義的方法的偏移量也總是一個(gè)定值。 Person 或 Object中的任意一個(gè)方法,在它們的方法表和其子類(lèi) 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 類(lèi)的時(shí)候,生成 girl.speak()的方法調(diào)用假設(shè)為:

Invokevirtual #12

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

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

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

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

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

接口調(diào)用

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

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

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()在類(lèi) Dancer 和 Snake 的方法表中的位置已經(jīng)不一樣了,顯然我們無(wú)法僅根據(jù)偏移量來(lái)進(jìn)行方法的調(diào)用。

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

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

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

相關(guān)文章

  • 詳解Spring加載Properties配置文件的四種方式

    詳解Spring加載Properties配置文件的四種方式

    這篇文章主要介紹了詳解Spring加載Properties配置文件的四種方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Java繪圖技術(shù)的詳解及實(shí)例

    Java繪圖技術(shù)的詳解及實(shí)例

    這篇文章主要介紹了Java繪圖技術(shù)的詳解及實(shí)例的相關(guān)資料,這里主要詳解Graphics類(lèi)的使用,需要的朋友可以參考下
    2017-08-08
  • java字符串所有操作方法匯總

    java字符串所有操作方法匯總

    這篇文章主要介紹了java字符串所有操作方法匯總,需要的朋友可以參考下,在實(shí)際的編程中,我們經(jīng)常需要對(duì)字符串進(jìn)行各種操作,例如連接、截取、替換等,本文將按類(lèi)別介紹一些Java字符串的常用方法,幫助讀者更好地理解和使用
    2023-11-11
  • Java中List對(duì)象集合按對(duì)象中某字段進(jìn)行排序舉例

    Java中List對(duì)象集合按對(duì)象中某字段進(jìn)行排序舉例

    這篇文章主要給大家介紹了關(guān)于Java中List對(duì)象集合按對(duì)象中某字段進(jìn)行排序的相關(guān)資料,我們?cè)谌粘i_(kāi)發(fā)中也經(jīng)常會(huì)用到排序算法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Java中常用加密/解密方法詳解

    Java中常用加密/解密方法詳解

    本文主要介紹了Java中常用加密/解密方法。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • 一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細(xì)教程

    一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細(xì)教程

    使用SSM(Spring、SpringMVC和Mybatis)已經(jīng)有段時(shí)間了,項(xiàng)目在技術(shù)上已經(jīng)沒(méi)有什么難點(diǎn)了,基于現(xiàn)有的技術(shù)就可以實(shí)現(xiàn)想要的功能,下面這篇文章主要給大家介紹了關(guān)于整合SSM框架:Spring MVC + Spring + MyBatis的相關(guān)資料,需要的朋友可以參考下。
    2017-07-07
  • Java?入門(mén)圖形用戶界面設(shè)計(jì)之事件處理下

    Java?入門(mén)圖形用戶界面設(shè)計(jì)之事件處理下

    圖形界面(簡(jiǎn)稱GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶來(lái)說(shuō)在視覺(jué)上更易于接受,本篇精講Java語(yǔ)言中關(guān)于圖形用戶界面的事件處理
    2022-02-02
  • Java實(shí)現(xiàn)后端跨域的常見(jiàn)解決方案

    Java實(shí)現(xiàn)后端跨域的常見(jiàn)解決方案

    跨源資源共享(CORS——Cross-Origin Resource Sharing,跨源資源共享,或通俗地譯為跨域資源共享)是一種基于 HTTP 頭的機(jī)制,跨域的解決方案有很多種,前后端都有,本文給大家主要介紹Java實(shí)現(xiàn)后端跨域的常見(jiàn)解決方案,需要的朋友可以參考下
    2024-04-04
  • 理解Java中的靜態(tài)綁定和動(dòng)態(tài)綁定

    理解Java中的靜態(tài)綁定和動(dòng)態(tài)綁定

    這篇文章主要幫助大家理解Java中的靜態(tài)綁定和動(dòng)態(tài)綁定,在Java中存在兩種綁定方式,一種為靜態(tài)綁定,另一種就是動(dòng)態(tài)綁定,亦稱為后期綁定,感興趣的小伙伴們可以參考一下
    2016-02-02
  • 使用Feign實(shí)現(xiàn)微服務(wù)間文件下載

    使用Feign實(shí)現(xiàn)微服務(wù)間文件下載

    這篇文章主要為大家詳細(xì)介紹了使用Feign實(shí)現(xiàn)微服務(wù)間文件下載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04

最新評(píng)論