摘要:重寫(xiě)語(yǔ)言中的定義子類(lèi)方法有一個(gè)方法與父類(lèi)方法的名字相同且參數(shù)類(lèi)型相同。父類(lèi)方法的返回值可以替換掉子類(lèi)方法的返回值。思維導(dǎo)圖參考文檔極客時(shí)間深入拆解虛擬機(jī)是如何執(zhí)行方法調(diào)用的上廣告
原文
回顧Java語(yǔ)言中的重載與重寫(xiě),并且看看JVM是怎么處理它們的。
重載Overload定義:
在同一個(gè)類(lèi)中有多個(gè)方法,它們的名字相同,但是參數(shù)類(lèi)型不同。
或者,父子類(lèi)中,子類(lèi)有一個(gè)方法與父類(lèi)非私有方法名字相同,但是參數(shù)類(lèi)型不同。那么子類(lèi)的這個(gè)方法對(duì)父類(lèi)方法構(gòu)成重載。
JVM是怎么處理重載的?其實(shí)是編譯階段編譯器就已經(jīng)決定好調(diào)用哪一個(gè)重載方法??聪旅娲a:
class Overload { void invoke(Object obj, Object... args) { } void invoke(String s, Object obj, Object... args) { } void test1() { // 調(diào)用第二個(gè) invoke 方法 invoke(null, 1); } void test2() { // 調(diào)用第二個(gè) invoke 方法 invoke(null, 1, 2); } void test3() { // 只有手動(dòng)繞開(kāi)可變長(zhǎng)參數(shù)的語(yǔ)法糖,才能調(diào)用第一個(gè)invoke方法 invoke(null, new Object[]{1}); } }
上面的注釋告訴了我們結(jié)果,那么怎么才能證明上面的注釋呢?我們利用javap觀察字節(jié)碼可以知道。
$ javac Overload.java $ javap -c Overload.java Compiled from "Overload.java" class Overload { ... void invoke(java.lang.Object, java.lang.Object...); Code: 0: return void invoke(java.lang.String, java.lang.Object, java.lang.Object...); Code: 0: return void test1(); Code: ... 10: invokevirtual #4 // Method invoke:(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V 13: return void test2(); Code: ... 17: invokevirtual #4 // Method invoke:(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V 20: return void test3(); Code: ... 13: invokevirtual #5 // Method invoke:(Ljava/lang/Object;[Ljava/lang/Object;)V 16: return }
這里面有很多JVM指令,你暫且不用關(guān)心,我們看test1、test2、test3方法調(diào)用的是哪個(gè)方法:
void test1(); Code: ... 10: invokevirtual #4 // Method invoke:(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V 13: return
invoke是方法名,(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V則是方法描述符。這里翻譯過(guò)來(lái)就是void invoke(String, Object, Object[]),Java的可變長(zhǎng)參數(shù)實(shí)際上就是數(shù)組,所以等同于void invoke(String, Object, Object...)。同理,test2調(diào)用的是void invoke(String, Object, Object...),test3調(diào)用的是void invoke(Object, Object...)。關(guān)于方法描述符的詳參JVM Spec - 4.3.2. Field Descriptors和JVM Spec - 4.3.3. Method Descriptors。
所以重載方法的選擇是在編譯過(guò)程中就已經(jīng)決定的,下面是編譯器的匹配步驟:
不允許自動(dòng)拆裝箱,不允許可變長(zhǎng)參數(shù),嘗試匹配
如果沒(méi)有匹配到,則允許自動(dòng)拆裝箱,不允許可變長(zhǎng)參數(shù),嘗試匹配
如果沒(méi)有匹配到,則允許自動(dòng)拆裝箱,允許可變長(zhǎng)參數(shù),嘗試匹配
注意:編譯器是根據(jù)實(shí)參類(lèi)型來(lái)匹配,實(shí)參類(lèi)型和實(shí)際類(lèi)型不是一個(gè)概念
如果在一個(gè)步驟里匹配到了多個(gè)方法,則根據(jù)形參類(lèi)型來(lái)找最貼切的。在上面的例子中第一個(gè)invoke的參數(shù)是Object, Object...,第二個(gè)invoke的參數(shù)是String, Object, Object...,兩個(gè)方法的第一個(gè)參數(shù)String是Object的子類(lèi),因此更為貼切,所以invoke(null, 1, 2)會(huì)匹配到第二個(gè)invoke方法上。
重寫(xiě)OverrideJava語(yǔ)言中的定義:
子類(lèi)方法有一個(gè)方法與父類(lèi)方法的名字相同且參數(shù)類(lèi)型相同。
父類(lèi)方法的返回值可以替換掉子類(lèi)方法的返回值。也就是說(shuō)父類(lèi)方法的返回值類(lèi)型:
要么和子類(lèi)方法返回值類(lèi)型一樣。
要么是子類(lèi)方法返回值類(lèi)型的父類(lèi)。
兩者都是非私有、非靜態(tài)方法。
(更多詳細(xì)信息可參考Java Language Spec - 8.4.8. Inheritance, Overriding, and Hiding,這里除了有更精確詳細(xì)的重寫(xiě)的定義,同時(shí)包含了范型方法的重寫(xiě)定義。)
但是JVM中對(duì)于重寫(xiě)的定義則有點(diǎn)不同:
子類(lèi)方法的名字與方法描述符與父類(lèi)方法相同。
兩者都是非私有、非靜態(tài)方法。
(更多詳細(xì)信息可參考JVM Spec - 5.4.5. Overriding)
注意上面提到的方法描述符,前面講過(guò)方法描述符包含了參數(shù)類(lèi)型及返回值,JVM要求這兩個(gè)必須完全相同才可以,但是Java語(yǔ)言說(shuō)的是參數(shù)類(lèi)型相同但是返回值類(lèi)型可以不同。Java編譯器通過(guò)創(chuàng)建Bridge Method來(lái)解決這個(gè)問(wèn)題,看下面代碼:
class A { Object f() { return null; } } class C extends A { Integer f() { return null; } }
然后用javap查看編譯結(jié)果:
$ javac Override.java $ javap -v C.class class C extends A ... { java.lang.Integer f(); descriptor: ()Ljava/lang/Integer; flags: Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn ... java.lang.Object f(); descriptor: ()Ljava/lang/Object; flags: ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokevirtual #2 // Method f:()Ljava/lang/Integer; 4: areturn LineNumberTable: line 7: 0 }
可以看到編譯器替我們創(chuàng)建了一個(gè)Object f()的Bridge Method,它調(diào)用的是Integer f(),這樣就構(gòu)成了JVM所定義的重寫(xiě)。
思維導(dǎo)圖 參考文檔極客時(shí)間 - 深入拆解 Java 虛擬機(jī) - 04 | JVM是如何執(zhí)行方法調(diào)用的?(上)
JVM Spec - 4.3.2. Field Descriptors
JVM Spec - 4.3.3. Method Descriptors
Java Language Spec - 8.4.8. Inheritance, Overriding, and Hiding
Java Language Spec - 8.4.9. Overloading
JVM Spec - 5.4.5. Overriding
Effects of Type Erasure and Bridge Methods
廣告文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/73367.html
摘要:中,任何未處理的受檢查異常強(qiáng)制在子句中聲明。運(yùn)行時(shí)多態(tài)是面向?qū)ο笞罹璧臇|西,要實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)需要方法重寫(xiě)子類(lèi)繼承父類(lèi)并重寫(xiě)父類(lèi)中已 1、簡(jiǎn)述Java程序編譯和運(yùn)行的過(guò)程:答:① Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼--字節(jié)碼,創(chuàng)建完源文件之后,程序會(huì)先被編譯成 .class 文件。② 在編譯好的java程序得到.class文件后,使用命令java 運(yùn)行這個(gè) .c...
摘要:中,任何未處理的受檢查異常強(qiáng)制在子句中聲明。運(yùn)行時(shí)多態(tài)是面向?qū)ο笞罹璧臇|西,要實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)需要方法重寫(xiě)子類(lèi)繼承父類(lèi)并重寫(xiě)父類(lèi)中已 1、簡(jiǎn)述Java程序編譯和運(yùn)行的過(guò)程:答:① Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼--字節(jié)碼,創(chuàng)建完源文件之后,程序會(huì)先被編譯成 .class 文件。② 在編譯好的java程序得到.class文件后,使用命令java 運(yùn)行這個(gè) .c...
摘要:對(duì)應(yīng)的代碼接下來(lái)的句是關(guān)鍵部分,兩句分分別把剛剛創(chuàng)建的兩個(gè)對(duì)象的引用壓到棧頂。所以雖然指令的調(diào)用是相同的,但行調(diào)用方法時(shí),此時(shí)棧頂存放的對(duì)象引用是,行則是。這,就是語(yǔ)言中方法重寫(xiě)的本質(zhì)。 類(lèi)初始化 在講類(lèi)的初始化之前,我們先來(lái)大概了解一下類(lèi)的聲明周期。如下圖 類(lèi)的聲明周期可以分為7個(gè)階段,但今天我們只講初始化階段。我們我覺(jué)得出來(lái)使用和卸載階段外,初始化階段是最貼近我們平時(shí)學(xué)的,也是筆試...
摘要:也就是說(shuō),一個(gè)實(shí)例變量,在的對(duì)象初始化過(guò)程中,最多可以被初始化次。當(dāng)所有必要的類(lèi)都已經(jīng)裝載結(jié)束,開(kāi)始執(zhí)行方法體,并用創(chuàng)建對(duì)象。對(duì)子類(lèi)成員數(shù)據(jù)按照它們聲明的順序初始化,執(zhí)行子類(lèi)構(gòu)造函數(shù)的其余部分。 類(lèi)的拷貝和構(gòu)造 C++是默認(rèn)具有拷貝語(yǔ)義的,對(duì)于沒(méi)有拷貝運(yùn)算符和拷貝構(gòu)造函數(shù)的類(lèi),可以直接進(jìn)行二進(jìn)制拷貝,但是Java并不天生支持深拷貝,它的拷貝只是拷貝在堆上的地址,不同的變量引用的是堆上的...
摘要:網(wǎng)站的面試專(zhuān)題學(xué)習(xí)筆記非可變性和對(duì)象引用輸出為,前后皆有空格。假定??臻g足夠的話(huà),盡管遞歸調(diào)用比較難以調(diào)試,在語(yǔ)言中實(shí)現(xiàn)遞歸調(diào)用也是完全可行的。棧遵守規(guī)則,因此遞歸調(diào)用方法能夠記住調(diào)用者并且知道此輪執(zhí)行結(jié)束之返回至當(dāng)初的被調(diào)用位置。 ImportNew 網(wǎng)站的Java面試專(zhuān)題學(xué)習(xí)筆記 1. 非可變性和對(duì)象引用 String s = Hello ; s += World ; s.tr...
閱讀 3241·2021-11-24 10:24
閱讀 3116·2021-11-11 16:54
閱讀 3172·2021-09-22 15:55
閱讀 2094·2019-08-30 15:44
閱讀 1976·2019-08-29 18:41
閱讀 2828·2019-08-29 13:43
閱讀 3142·2019-08-29 12:51
閱讀 1338·2019-08-26 12:19