日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?...

發(fā)布時(shí)間:2023/11/27 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前語:微信改版后,大量讀者還沒養(yǎng)成點(diǎn)贊的習(xí)慣,如寫得好,望大家閱讀后在右下邊“好看”處點(diǎn)個(gè)贊,以示鼓勵(lì)!長(zhǎng)期堅(jiān)持原創(chuàng)真的很不容易,多次想放棄,堅(jiān)持是一種信仰,專注是一種態(tài)度。

關(guān)于寫這篇文章,是來自于一個(gè)同學(xué)在群里拋出這么一道面試題,問執(zhí)行結(jié)果是什么?

public class OverloadTest {    static abstract class A{}    static class B extends A {}    static class C extends A {}    public void sayHello(A a){        System.out.println("a");    }    public void sayHello(B a){        System.out.println("b");    }    public void sayHello(C b){        System.out.println("c");    }    // Object 參數(shù)    public void say(Object arg) {        System.out.println("hello object");    }    // int 參數(shù)    public void say(int arg) {        System.out.println("hello int");    }    // long 參數(shù)    public void say(long arg) {        System.out.println("hello long");    }    // char 參數(shù)    public void say(char arg) {        System.out.println("hello char");    }    // Character 參數(shù)    public void say(Character arg) {        System.out.println("hello character");    }    // 變長(zhǎng)參數(shù)    public void say(char... arg) {        System.out.println("hello char...");    }    // Serializable 參數(shù)    public void say(Serializable arg) {        System.out.println("hello serializable");    }    public static void main(String[] args) {        OverloadTest overloadTest = new OverloadTest();        overloadTest.say('a');        overloadTest.say("a");        A b = new B();        A c = new C();        overloadTest.sayHello(b);        overloadTest.sayHello(c);        overloadTest.sayHello((B)b);    }}

輸出的結(jié)果如下。

hello charhello serializableaab

很明顯涉及到方法重載(overload),為什么會(huì)是這個(gè)結(jié)果?要從我們開始學(xué)Java的時(shí)說起,那時(shí)老師就告訴我們兩個(gè)結(jié)論。

  1. javac編譯器在編譯階段會(huì)根據(jù)參數(shù)的靜態(tài)類型來決定選擇哪個(gè)重載版本。

  2. 重載優(yōu)先級(jí),先匹配參數(shù)個(gè)數(shù);再匹配參數(shù)類型的直接所屬類;如果沒有找到直接的所屬類,會(huì)向上轉(zhuǎn)型(包裝類 -> 接口 -> 父類 );如果向上轉(zhuǎn)型無果,再查找可變參數(shù)列表;以上都找不到,則報(bào)找不到方法錯(cuò)誤。

上面提到了靜態(tài)類型,我舉列說明一下。

A b = new B();

這里的A就是靜態(tài)類型,編譯階段可確定;那么相反B就是實(shí)際類型,只能運(yùn)行階段才能確定。

我估計(jì)知道答案的同學(xué)很多,但要搞明白整個(gè)底層原理的同學(xué)很少,這里涉及到Java方法底層調(diào)用的原理。

方法調(diào)用

其實(shí)說白了,JVM調(diào)用Java程序時(shí),其實(shí)也是執(zhí)行的機(jī)器指令,利用字節(jié)碼解釋器作為跨越字節(jié)碼與機(jī)器指令的橋梁,也就是說一個(gè)字節(jié)碼對(duì)應(yīng)一段特定邏輯的本地機(jī)器指令,而JVM在解釋執(zhí)行字節(jié)碼指令時(shí),會(huì)直接調(diào)用字節(jié)碼所對(duì)應(yīng)的機(jī)器指令。關(guān)于它是怎么調(diào)用的?如果你感興趣的話,可以去了解一下C的函數(shù)指針,它其實(shí)就是將函數(shù)指針指向這段機(jī)器指令的首地址,從而實(shí)現(xiàn)C語言直接調(diào)用機(jī)器指令的目的(以前寫exp經(jīng)常這么干)。

我承認(rèn)上面這段,有點(diǎn)難。

簡(jiǎn)而言之,Java調(diào)用方法其實(shí)用到了字節(jié)碼指令,最終查找相應(yīng)的機(jī)器指令,來實(shí)現(xiàn)方法的調(diào)用。

那么關(guān)于方法調(diào)用,Java提供了5個(gè)字節(jié)碼指令。

  1. invokestatic:調(diào)用類方法(編譯階段確定方法調(diào)用版本)。

  2. invokespecial:調(diào)用構(gòu)造器方法、私有方法及父類方法(編譯階段確定方法調(diào)用版本)。

  3. invokevirtual:調(diào)用實(shí)例方法(虛方法)。

  4. invokeinterface:調(diào)用接口方法,在運(yùn)行再確定一個(gè)實(shí)現(xiàn)此接口的對(duì)象。

  5. invokedynamic:由用戶引導(dǎo)方法決定。

invokestatic和invokespecial指令在類加載時(shí),就能把符號(hào)引用(即邏輯地址,與虛擬機(jī)內(nèi)存無關(guān))解析為直接引用,符合這個(gè)條件的有靜態(tài)方法、實(shí)例構(gòu)造器方法、私有方法、父類方法這4類,叫非虛方法。

非虛方法除了上面靜態(tài)方法、實(shí)例構(gòu)造器方法、私有方法、父類方法這4種方法之外,還包括final方法。雖然final方法使用invokevirtual指令來調(diào)用,但是final方法無法被覆蓋,沒有其他版本,無需對(duì)方法接收者進(jìn)行多態(tài)選擇,或者說多態(tài)選擇的結(jié)果是唯一的。

底層實(shí)現(xiàn)

要看它底層的實(shí)現(xiàn),我們還是得要看字節(jié)碼,我通過javap工具把main方法的字節(jié)碼給各位展示出來,如下所示。

public static void main(java.lang.String[]);    descriptor: ([Ljava/lang/String;)V    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=2, locals=4, args_size=1         0: new           #14                 // class com/yrzx404/base/code/OverloadTest         3: dup         4: invokespecial #15                 // Method "":()V         7: astore_1         8: aload_1         9: bipush        97        11: invokevirtual #16                 // Method say:(C)V        14: aload_1        15: ldc           #3                  // String a        17: invokevirtual #17                 // Method say:(Ljava/io/Serializable;)V        20: new           #18                 // class com/yrzx404/base/code/OverloadTest$B        23: dup        24: invokespecial #19                 // Method com/yrzx404/base/code/OverloadTest$B."":()V        27: astore_2        28: new           #20                 // class com/yrzx404/base/code/OverloadTest$C        31: dup        32: invokespecial #21                 // Method com/yrzx404/base/code/OverloadTest$C."":()V        35: astore_3        36: aload_1        37: aload_2        38: invokevirtual #22                 // Method sayHello:(Lcom/yrzx404/base/code/OverloadTest$A;)V        41: aload_1        42: aload_3        43: invokevirtual #22                 // Method sayHello:(Lcom/yrzx404/base/code/OverloadTest$A;)V        46: aload_1        47: aload_2        48: checkcast     #18                 // class com/yrzx404/base/code/OverloadTest$B        51: invokevirtual #23                 // Method sayHello:(Lcom/yrzx404/base/code/OverloadTest$B;)V        54: return      LineNumberTable:        line 67: 0        line 68: 8        line 69: 14        line 71: 20        line 72: 28        line 73: 36        line 74: 41        line 75: 46        line 76: 54      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      55     0  args   [Ljava/lang/String;            8      47     1 overloadTest   Lcom/yrzx404/base/code/OverloadTest;           28      27     2     b   Lcom/yrzx404/base/code/OverloadTest$A;           36      19     3     c   Lcom/yrzx404/base/code/OverloadTest$A;

我們這段字節(jié)碼指令可以得出,invokevirtual已經(jīng)確定了調(diào)用方法,并且是根據(jù)方法參數(shù)的靜態(tài)類型來決定的。

這里也解決了之前大家的疑問,overloadTest.sayHello((B)b),為什么結(jié)果為b?主要在這兩句字節(jié)碼指令起的作用。

48: checkcast     #18                 // class com/yrzx404/base/code/OverloadTest$B51: invokevirtual #23                 // Method sayHello:(Lcom/yrzx404/base/code/OverloadTest$B;)V

即在強(qiáng)制類型轉(zhuǎn)換時(shí),會(huì)有指令checkcast的調(diào)用,而且invokevirtual指令的調(diào)用方法也會(huì)發(fā)生了變化。

關(guān)于重寫優(yōu)先級(jí),這是詹爺他們定下的規(guī)定,沒有什么好說的,記住就好了。

最后

相信看到這里,大家應(yīng)該明白了方法重載。說實(shí)話,我個(gè)人覺得這道面試題除了為難面試者,沒有什么鳥用!因?yàn)閷?shí)際工作中,沒有誰閑的蛋疼去寫這種“炫技”的代碼。

那么關(guān)于方法重寫(override),我這里給一個(gè)結(jié)論吧,重寫方法的調(diào)用主要看實(shí)際類型,在運(yùn)行時(shí)決定調(diào)用版本。實(shí)際類型如果實(shí)現(xiàn)了該方法則直接調(diào)用該方法,如果沒有實(shí)現(xiàn),則在繼承關(guān)系中從低到高搜索有無實(shí)現(xiàn)。

那么,希望你也能就這我的思路去分析一下方法重寫。

熬夜寫文章,各位讀者記得在右下角點(diǎn)下【好看】以示鼓勵(lì)!

---END---

總結(jié)

以上是生活随笔為你收集整理的没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。