亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java和JVM的重載識(shí)別,重寫方法是怎樣進(jìn)行的

 更新時(shí)間:2022年01月21日 11:19:47   作者:JavaEdge.?  
這篇文章主要介紹了Java和JVM的重載識(shí)別,重寫方法是怎樣進(jìn)行的,違章圍繞了Java和JVM的重載識(shí)別,重寫方法展開(kāi)相關(guān)資料,需要的小伙伴可以參考一下,希望對(duì)你的工作或?qū)W習(xí)有所幫助

可變長(zhǎng)參數(shù)方法的重載造成的。

1.案例

void invoke(Object obj, Object... args) { ... }
void invoke(String s, Object obj, Object... args) { ... }

invoke(null, 1); ? ?// 調(diào)用第二個(gè)invoke方法
invoke(null, 1, 2); // 調(diào)用第二個(gè)invoke方法
invoke(null, new Object[]{1}); // 只有手動(dòng)繞開(kāi)可變長(zhǎng)參數(shù)的語(yǔ)法糖,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 才能調(diào)用第一個(gè)invoke方法

某API定義了兩個(gè)同名重載方法:

  • 第一個(gè)接收一個(gè)Object,以及聲明為Object…的變長(zhǎng)參數(shù)
  • 第二個(gè)則接收一個(gè)String、一個(gè)Object,以及聲明為Object…的變長(zhǎng)參數(shù)

想調(diào)用第一個(gè)方法,傳參(null, 1),即聲明為Object的形式參數(shù)所對(duì)應(yīng)的實(shí)際參數(shù)為null,而變長(zhǎng)參數(shù)則對(duì)應(yīng)1。
之所以不提倡可變長(zhǎng)參數(shù)方法重載,是因?yàn)镴ava編譯器可能無(wú)法決定應(yīng)該調(diào)用哪個(gè)目標(biāo)方法。
這種情況下,編譯器會(huì)報(bào)錯(cuò),并且提示這方法調(diào)用有二義性。然而,Java編譯器直接將我的方法調(diào)用識(shí)別為調(diào)用第二個(gè)方法,這究竟是為什么呢?

Java虛擬機(jī)是怎么識(shí)別目標(biāo)方法的?

2.重載與重寫

同一類中出現(xiàn)多個(gè):

  • 名字相同
  • 參數(shù)類型相同

這限制可通過(guò)字節(jié)碼工具繞開(kāi),編譯完成后,可再向class文件中添加方法名和參數(shù)類型相同,而返回類型不同的方法。當(dāng)這種包括多個(gè)方法名相同、參數(shù)類型相同,而返回類型不同的方法的類,出現(xiàn)在Java編譯器的用戶類路徑上時(shí),它是怎么確定需要調(diào)用哪個(gè)方法的呢?
當(dāng)前版本的Java編譯器會(huì)直接選取第一個(gè)方法名以及參數(shù)類型匹配的方法。并且,它會(huì)根據(jù)所選取方法的返回類型來(lái)決定可不可以通過(guò)編譯,以及需不需要進(jìn)行值轉(zhuǎn)換等。
重載的方法在編譯過(guò)程中即可完成識(shí)別。具體到每一個(gè)方法調(diào)用,Java編譯器會(huì)根據(jù)所傳入?yún)?shù)的聲明類型(注意與實(shí)際類型區(qū)分)來(lái)選取重載方法。選取的過(guò)程共分為三個(gè)階段:

  • 在不考慮對(duì)基本類型自動(dòng)裝拆箱及可變長(zhǎng)參數(shù)情況下選取重載方法
  • 如在第1個(gè)階段沒(méi)找到適配方法,那在允許自動(dòng)裝拆箱,但不允許可變長(zhǎng)參數(shù)情況下選取重載方法
  • 如在第2個(gè)階段中沒(méi)找到適配方法,那在允許自動(dòng)裝拆箱及可變長(zhǎng)參數(shù)情況下選取重載方法

如Java編譯器在同一階段中找到多個(gè)適配方法,那它會(huì)在其中選擇一個(gè)最為貼切,貼切程度關(guān)鍵就是形式參數(shù)類型的繼承關(guān)系。

傳入null時(shí),它既可匹配第一個(gè)方法中聲明為Object的形式參數(shù),也可匹配第二個(gè)方法中聲明為String的形式參數(shù)。由于String是Object的子類,因此Java編譯器會(huì)認(rèn)為第二個(gè)方法更貼切。
除同一個(gè)類中的方法,重載也可作用于這個(gè)類所繼承而來(lái)的方法。如子類定義了與父類中非私有方法同名的方法,且這兩個(gè)方法的參數(shù)類型不同,那在子類中,這兩個(gè)方法同樣構(gòu)成重載。

若子類定義與父類中非private方法的同名方法,且這兩方法參數(shù)類型相同,那這倆方法間啥關(guān)系:

  • 若這倆都是static方法,那子類中的方法隱藏了父類中的方法
  • 若都不是 static 的,則子類的方法重寫了父類中的方法

Java的方法重寫是多態(tài)的體現(xiàn):允許子類在繼承父類部分功能同時(shí),擁有自己獨(dú)特行為。
重寫調(diào)用會(huì)根據(jù)調(diào)用者的動(dòng)態(tài)類型選取實(shí)際的目標(biāo)方法。

3.JVM的靜態(tài)綁定和動(dòng)態(tài)綁定

Java虛擬機(jī)識(shí)別方法的關(guān)鍵在于類名、方法名及方法描述符(method descriptor)。
方法描述符由方法的參數(shù)類型及返回類型構(gòu)成。
同一類中,如同時(shí)出現(xiàn)多個(gè)名字相同且描述符相同的方法,那Java虛擬機(jī)會(huì)在類的驗(yàn)證階段報(bào)錯(cuò)。
Java虛擬機(jī)與Java語(yǔ)言不同,它不限制名字與參數(shù)類型相同,但返回類型不同的方法出現(xiàn)在同一類,對(duì)調(diào)用這些方法的字節(jié)碼,由于字節(jié)碼所附帶的方法描述符包含了返回類型,因此Java虛擬機(jī)能夠準(zhǔn)確識(shí)別目標(biāo)方法。

JVM方法重寫判定同樣基于方法描述符。
如子類定義了與父類中非私有、非靜態(tài)方法同名的方法,則僅當(dāng)這倆方法的參數(shù)類型及返回類型一致,JVM才會(huì)判定為重寫。

對(duì)Java中重寫而Java虛擬機(jī)中非重寫的情況,編譯器會(huì)通過(guò)生成橋接方法[2]實(shí)現(xiàn)Java的重寫語(yǔ)義。

由于對(duì)重載方法的區(qū)分在編譯階段已完成,可認(rèn)為JVM不存在重載概念。因此,某些文章將

  • 重載稱為靜態(tài)綁定(static binding)或編譯時(shí)多態(tài)(compile-time polymorphism)
  • 重寫稱為動(dòng)態(tài)綁定(dynamic binding)

這說(shuō)法在JVM語(yǔ)境下并非完全正確,因?yàn)槟愁愔械闹剌d方法可能被它的子類重寫,因此JVM 會(huì)將所有對(duì)非私有實(shí)例方法的調(diào)用編譯為需要?jiǎng)討B(tài)綁定的類型。

JVM的:

  • 靜態(tài)綁定指在解析時(shí)便能夠直接識(shí)別目標(biāo)方法
  • 動(dòng)態(tài)綁定指要在運(yùn)行過(guò)程中,根據(jù)調(diào)用者的動(dòng)態(tài)類型來(lái)識(shí)別目標(biāo)方法

Java字節(jié)碼中與調(diào)用相關(guān)的指令有:

  • invokestatic:調(diào)用靜態(tài)方法
  • invokespecial:調(diào)用私有實(shí)例方法、構(gòu)造器及使用super關(guān)鍵字調(diào)用父類的實(shí)例方法或構(gòu)造器,和所實(shí)現(xiàn)接口的默認(rèn)方法
  • invokevirtual:用于調(diào)用非私有實(shí)例方法
  • invokeinterface:用于調(diào)用接口方法
  • invokedynamic:用于調(diào)用動(dòng)態(tài)方法較為復(fù)雜

編譯生成這四種調(diào)用指令的情況。

interface 客戶 {
? boolean isVIP();
}

class 商戶 {
? public double 折后價(jià)格(double 原價(jià), 客戶 某客戶) {
? ? return 原價(jià) * 0.8d;
? }
}

class 奸商 extends 商戶 {
? @Override
? public double 折后價(jià)格(double 原價(jià), 客戶 某客戶) {
? ? if (某客戶.isVIP()) { ? ? ? ? ? ? ? ? ? ? ? ? // invokeinterface ? ? ?
? ? ? return 原價(jià) * 價(jià)格歧視(); ? ? ? ? ? ? ? ? ? ?// invokestatic
? ? } else {
? ? ? return super.折后價(jià)格(原價(jià), 某客戶); ? ? ? ? ?// invokespecial
? ? }
? }
? public static double 價(jià)格歧視() {
? ? // 咱們的殺熟算法太粗暴了,應(yīng)該將客戶城市作為隨機(jī)數(shù)生成器的種子。
? ? return new Random() ? ? ? ? ? ? ? ? ? ? ? ? ?// invokespecial
? ? ? ? ? ?.nextDouble() ? ? ? ? ? ? ? ? ? ? ? ? // invokevirtual
? ? ? ? ? ?+ 0.8d;
? }
}

“商戶”類定義了一個(gè)成員方法,叫“折后價(jià)格”,它接收一個(gè)double類型參數(shù)及一個(gè)“客戶”類型參數(shù)。
這里“客戶”是個(gè)接口,定義了一個(gè)接口方法“isVIP”。

“奸商”類這個(gè)方法,首先調(diào)用客戶#isVIP,該調(diào)用會(huì)被編譯為invokeinterface指令

  • 若客戶是VIP,則調(diào)用奸商類的一個(gè)名叫“價(jià)格歧視”的靜態(tài)方法。該調(diào)用會(huì)被編譯為invokestatic指令
  • 如客戶不是VIP,則通過(guò)super調(diào)用父類的“折后價(jià)格”方法。該調(diào)用會(huì)被編譯為invokespecial指令

在靜態(tài)方法“價(jià)格歧視”會(huì)調(diào)用Random類的構(gòu)造器。該調(diào)用會(huì)被編譯為invokespecial指令。然后以這個(gè)新建Random對(duì)象為調(diào)用者,調(diào)用Random類中的nextDouble方法。該調(diào)用會(huì)被編譯為invokevirutal指令。

對(duì)于invokestatic以及invokespecial而言,Java虛擬機(jī)能夠直接識(shí)別具體的目標(biāo)方法。

而對(duì)于invokevirtual以及invokeinterface而言,在絕大部分情況下,虛擬機(jī)需要在執(zhí)行過(guò)程中,根據(jù)調(diào)用者的動(dòng)態(tài)類型,來(lái)確定具體的目標(biāo)方法。

如虛擬機(jī)能確定目標(biāo)方法有且僅有一個(gè),比如說(shuō)目標(biāo)方法被標(biāo)記為final[3][4],它可不通過(guò)動(dòng)態(tài)類型,直接確定目標(biāo)方法。

4.調(diào)用指令的符號(hào)引用

編譯過(guò)程中,我們并不知目標(biāo)方法的具體內(nèi)存地址。因此,Java編譯器會(huì)暫時(shí)用符號(hào)引表示該目標(biāo)方法。
這符號(hào)引用包括目標(biāo)方法所在的類或接口的名字,以及目標(biāo)方法的方法名和方法描述符。

符號(hào)引用存儲(chǔ)在class文件的常量池。根據(jù)目標(biāo)方法是否為接口方法,這些引用可分為:

  • 接口符號(hào)引用
  • 非接口符號(hào)引用
// 在奸商.class的常量池中,#16為接口符號(hào)引用,指向接口方法"客戶.isVIP()"。#22為非接口符號(hào)引用,指向靜態(tài)方法"奸商.價(jià)格歧視()"。
$ javap -v 奸商.class ...
Constant pool:
...
? #16 = InterfaceMethodref #27.#29 ? ? ? ?// 客戶.isVIP:()Z
...
? #22 = Methodref ? ? ? ? ?#1.#33 ? ? ? ? // 奸商.價(jià)格歧視:()D
...

執(zhí)行使用了符號(hào)引用的字節(jié)碼前,JVM需解析這些【符號(hào)引用】并替換為【實(shí)際引用】。

對(duì)【非接口符號(hào)引用】,假定該【符號(hào)引用】所指向的類為C,則JVM按如下步驟查找:

  • 在C中查找符合名字及描述符的方法
  • 若沒(méi)找到,搜索C的父類,直至Object類
  • 若還沒(méi)找到,在C所直接實(shí)現(xiàn)或間接實(shí)現(xiàn)的接口中搜索,該步搜索得到的目標(biāo)方法必須是非private、非static且若目標(biāo)方法在間接實(shí)現(xiàn)的接口中,則需滿足C與該接口間無(wú)其他符合條件的目標(biāo)方法。若有多個(gè)符合條件的目標(biāo)方法,則返回其中任一。

所以static方法也可通過(guò)子類來(lái)調(diào)用。子類的static方法會(huì)隱藏(這不是重寫)父類中的同名、同描述符的靜態(tài)方法。

對(duì)于接口符號(hào)引用,假定該符號(hào)引用所指向的接口為I,則Java虛擬機(jī)會(huì)按照如下步驟進(jìn)行查找。

  • 在I中查找符合名字及描述符的方法。
  • 如果沒(méi)有找到,在Object類中的公有實(shí)例方法中搜索。
  • 如果沒(méi)有找到,則在I的超接口中搜索。這一步的搜索結(jié)果的要求與非接口符號(hào)引用步驟3的要求一致。

經(jīng)過(guò)上述解析步驟后,符號(hào)引用會(huì)被解析成實(shí)際引用:

  • 對(duì)可靜態(tài)綁定的方法調(diào)用,實(shí)際引用是個(gè)指向方法的指針
  • 對(duì)需動(dòng)態(tài)綁定的方法調(diào)用,實(shí)際引用則是個(gè)方法表的索引

5.總結(jié)與實(shí)踐

文介紹了Java以及Java虛擬機(jī)是如何識(shí)別目標(biāo)方法的。

在Java方法的:

  • 重載,方法名相同而參數(shù)類型不相同的方法間
  • 重寫,方法名相同&參數(shù)類型也相同的方法間

JVM識(shí)別方法的方式除了方法名和參數(shù)類型,還有返回類型。

JVM的:

  • 靜態(tài)綁定:在解析時(shí)便能夠直接識(shí)別目標(biāo)方法的情況
  • 動(dòng)態(tài)綁定,需在運(yùn)行過(guò)程中根據(jù)調(diào)用者的動(dòng)態(tài)類型來(lái)識(shí)別目標(biāo)方法的情況。由于Java編譯器已區(qū)分重載方法,因此可認(rèn)為JVM不存在重載

在class文件中,Java編譯器會(huì)用符號(hào)引用指代目標(biāo)方法。在執(zhí)行調(diào)用指令前,它所附帶的符號(hào)引用需要被解析成實(shí)際引用。對(duì)于可以靜態(tài)綁定的方法調(diào)用而言,實(shí)際引用為目標(biāo)方法的指針。對(duì)于需要?jiǎng)討B(tài)綁定的方法調(diào)用而言,實(shí)際引用為輔助動(dòng)態(tài)綁定的信息。

Java的重寫與Java虛擬機(jī)中的重寫并不一致,但編譯器會(huì)通過(guò)生成橋接方法來(lái)彌補(bǔ)。

到此這篇關(guān)于Java和JVM的重載識(shí)別,重寫方法是怎樣進(jìn)行的的文章就介紹到這了,更多相關(guān)Java和JVM重載識(shí)別、重寫方法 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

參考:

[1] https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html
[2] https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html
[3] https://wiki.openjdk.java.net/display/HotSpot/VirtualCalls
[4] https://wiki.openjdk.java.net/display/HotSpot/InterfaceCalls

相關(guān)文章

  • IntelliJ IDEA基于Scala實(shí)現(xiàn)Git檢查工具

    IntelliJ IDEA基于Scala實(shí)現(xiàn)Git檢查工具

    這篇文章主要介紹了如何使用Scala實(shí)現(xiàn)自定義的Git檢查工具,大家可以基于本文的示例進(jìn)行擴(kuò)展與實(shí)現(xiàn),也可以進(jìn)行其他應(yīng)用方向的嘗試,感興趣的可以了解下
    2023-08-08
  • Java設(shè)計(jì)模式模板方法(Template)原理解析

    Java設(shè)計(jì)模式模板方法(Template)原理解析

    這篇文章主要介紹了Java設(shè)計(jì)模式模板方法(Template)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例

    java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)文件讀寫與壓縮實(shí)例,有助于讀者加深對(duì)文件操作的理解,需要的朋友可以參考下
    2014-07-07
  • Mybatis單個(gè)參數(shù)的if判斷報(bào)異常There is no getter for property named ''xxx'' in ''class java.lang.Integer''的解決方案

    Mybatis單個(gè)參數(shù)的if判斷報(bào)異常There is no getter for property named ''x

    今天小編就為大家分享一篇關(guān)于Mybatis單個(gè)參數(shù)的if判斷報(bào)異常There is no getter for property named 'xxx' in 'class java.lang.Integer'的解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 一篇文章帶你入門Java修飾符

    一篇文章帶你入門Java修飾符

    Java語(yǔ)言提供了很多修飾符,主要分為以下兩類:訪問(wèn)修飾符;非訪問(wèn)修飾符。修飾符用來(lái)定義類、方法或者變量,通常放在語(yǔ)句的最前端。我們通過(guò)下面的例子來(lái)說(shuō)明,下面就跟小編一起來(lái)看下吧
    2021-08-08
  • java 中volatile和lock原理分析

    java 中volatile和lock原理分析

    這篇文章主要介紹了java 中volatile和lock原理分析的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 實(shí)例分析Java單線程與多線程

    實(shí)例分析Java單線程與多線程

    本篇文章通過(guò)代碼實(shí)例給大家詳細(xì)講述了Java單線程與多線程的相關(guān)原理和知識(shí)點(diǎn)總結(jié),需要的朋友可以學(xué)習(xí)下。
    2018-02-02
  • Idea中如何調(diào)出Run dashboard 或services窗口

    Idea中如何調(diào)出Run dashboard 或services窗口

    這篇文章主要介紹了Idea中如何調(diào)出Run dashboard 或services窗口問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java8新特性之字符串去重介紹

    Java8新特性之字符串去重介紹

    這篇文章主要介紹了Java8新特性之字符串去重介紹,新的字符串去重特性可以幫助減少應(yīng)用中String對(duì)象的內(nèi)存占用,目前該特性只適用于G1垃圾收集器,并且默認(rèn)不被開(kāi)啟,需要的朋友可以參考下
    2014-09-09
  • java實(shí)現(xiàn)打磚塊小游戲

    java實(shí)現(xiàn)打磚塊小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)打磚塊小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評(píng)論