没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?...
前語:微信改版后,大量讀者還沒養(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é)論。
javac編譯器在編譯階段會(huì)根據(jù)參數(shù)的靜態(tài)類型來決定選擇哪個(gè)重載版本。
重載優(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é)碼指令。
invokestatic:調(diào)用類方法(編譯階段確定方法調(diào)用版本)。
invokespecial:調(diào)用構(gòu)造器方法、私有方法及父類方法(編譯階段確定方法調(diào)用版本)。
invokevirtual:調(diào)用實(shí)例方法(虛方法)。
invokeinterface:調(diào)用接口方法,在運(yùn)行再確定一個(gè)實(shí)現(xiàn)此接口的對(duì)象。
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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 魔鬼恋人原画是谁画的啊?
- 下一篇: spark提交到yarn_详细总结spa