日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

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

前語:微信改版后,大量讀者還沒養成點贊的習慣,如寫得好,望大家閱讀后在右下邊“好看”處點個贊,以示鼓勵!長期堅持原創真的很不容易,多次想放棄,堅持是一種信仰,專注是一種態度。

關于寫這篇文章,是來自于一個同學在群里拋出這么一道面試題,問執行結果是什么?

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 參數    public void say(Object arg) {        System.out.println("hello object");    }    // int 參數    public void say(int arg) {        System.out.println("hello int");    }    // long 參數    public void say(long arg) {        System.out.println("hello long");    }    // char 參數    public void say(char arg) {        System.out.println("hello char");    }    // Character 參數    public void say(Character arg) {        System.out.println("hello character");    }    // 變長參數    public void say(char... arg) {        System.out.println("hello char...");    }    // Serializable 參數    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);    }}

輸出的結果如下。

hello charhello serializableaab

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

  1. javac編譯器在編譯階段會根據參數的靜態類型來決定選擇哪個重載版本。

  2. 重載優先級,先匹配參數個數;再匹配參數類型的直接所屬類;如果沒有找到直接的所屬類,會向上轉型(包裝類 -> 接口 -> 父類 );如果向上轉型無果,再查找可變參數列表;以上都找不到,則報找不到方法錯誤。

上面提到了靜態類型,我舉列說明一下。

A b = new B();

這里的A就是靜態類型,編譯階段可確定;那么相反B就是實際類型,只能運行階段才能確定。

我估計知道答案的同學很多,但要搞明白整個底層原理的同學很少,這里涉及到Java方法底層調用的原理。

方法調用

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

我承認上面這段,有點難。

簡而言之,Java調用方法其實用到了字節碼指令,最終查找相應的機器指令,來實現方法的調用。

那么關于方法調用,Java提供了5個字節碼指令。

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

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

  3. invokevirtual:調用實例方法(虛方法)。

  4. invokeinterface:調用接口方法,在運行再確定一個實現此接口的對象。

  5. invokedynamic:由用戶引導方法決定。

invokestatic和invokespecial指令在類加載時,就能把符號引用(即邏輯地址,與虛擬機內存無關)解析為直接引用,符合這個條件的有靜態方法、實例構造器方法、私有方法、父類方法這4類,叫非虛方法。

非虛方法除了上面靜態方法、實例構造器方法、私有方法、父類方法這4種方法之外,還包括final方法。雖然final方法使用invokevirtual指令來調用,但是final方法無法被覆蓋,沒有其他版本,無需對方法接收者進行多態選擇,或者說多態選擇的結果是唯一的。

底層實現

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

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;

我們這段字節碼指令可以得出,invokevirtual已經確定了調用方法,并且是根據方法參數的靜態類型來決定的。

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

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

即在強制類型轉換時,會有指令checkcast的調用,而且invokevirtual指令的調用方法也會發生了變化。

關于重寫優先級,這是詹爺他們定下的規定,沒有什么好說的,記住就好了。

最后

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

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

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

熬夜寫文章,各位讀者記得在右下角點下【好看】以示鼓勵!

---END---

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。