從JVM看Java語言特性(一) 重載機制的實現(xiàn)

大家應該都知道, 在C語言中是沒有重載的, 因為在編譯過程中編譯器只檢查函數(shù)名, 不管參數(shù)是否一致. 因此編譯器無法區(qū)分兩個同名函數(shù)的不同. 有時候在同一函數(shù)中需要不同入參會比較麻煩, 需要在方法結尾加上點標識來實現(xiàn)重載, 比如:

_syscall0(type,name)
_syscall1(type,name,type1,arg1)
_syscall2(type,name,type1,arg1,type2,arg2)
_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)
_syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)
_syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)
_syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)

而在Java和C#中, 編譯器允許在同一個類中的多個方法使用相同的方法名, 編譯時通過不同的參數(shù)類型列表來區(qū)分. 例如在下面的例子中非常方便的重載了構造器, 來實現(xiàn)多種構造方法:

class Tree(){
    int height;

    public Tree(){
        height = 0;
    }

    public Tree(int initialHeight){
        height = initialHeight;
    }
}

這樣的寫法讓developer們不需要記一長串的方法名, 降低了編程的難度, 那么重載到底在JVM里面是如何實現(xiàn)的呢? 讓我們繼續(xù)深入~

Java在編譯時先將.java文件編譯成.class文件, 在class字節(jié)碼中, 方法簽名主要由三部分組成:

  1. 方法的標識(access_flag): public, private, static, final, synchronized, native等
  2. 方法的名稱(name)
  3. 方法的描述(descriptor), 包括方法的返回值類型和入參信息

這三個部分的內容可以唯一確定一個方法, 那么java重載的秘密也就揭開了, jvm在運行期間不僅檢查方法的名字, 同時還會區(qū)分入參和返回值.

我們可以實際看一下Tree.java 的字節(jié)碼文件來驗證一下, 用javap -verbose Tree.class 來解析Tree, 會發(fā)現(xiàn)這兩個構造器的字節(jié)碼是這樣的:

public Dao.Tree();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_0
         6: putfield      #2                  // Field height:I
         9: return
      LineNumberTable:
        line 7: 0
        line 8: 4
        line 9: 9

  public Dao.Tree(int);
    descriptor: (I)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iload_1
         6: putfield      #2                  // Field height:I
         9: return
      LineNumberTable:
        line 11: 0
        line 12: 4
        line 13: 9

可以看到對于第一個構造方法, 標識為public, 名字為Dao.Tree(自己加了一個叫Dao的package...), 方法描述叫作()V.

而對于第二個構造方法, 標識為public, 名字同樣為Dao.Tree, 方法描述叫作(I)V. 這里返回值int之所以叫作I是因為JVM規(guī)范中規(guī)定了基本類型的簡寫, 這里的I就是int 的簡寫.


我們理解了方法重載的原理之后, 由此繼續(xù)深入思考下去, 照這么理解的話方法的返回值也會影響重載, 理論上只要編譯器可以確定一個方法的返回值是可以實現(xiàn)不同返回值的重載的. 但是要是出現(xiàn)如下情況的話編譯器就會無法判斷到底應該用哪個重載方法:

    private int getHeight(String message){
        System.out.println("return int!");
        return height;
    }

    private Integer getHeight(String message){
        System.out.println("return Integer!");
        return height;
    }

    public static void main(String[] args) {
        Tree tree = new Tree();
        tree.getHeight("編譯器報錯");
    }

getHeight(String)這個方法中, 因為返回值沒有賦值給任何變量, 因此編譯器無法判斷返回值, 此時編譯器報錯, 錯誤提示是該方法已經定義, 無法重復定義.

可以看到, 返回值是無法作為重載的區(qū)分的, 這不是因為JVM里面不能同時存在這兩個方法, 而是因為在某些情況下無法判斷返回值, 導致編譯器干脆直接不允許這樣寫(編譯器內心: 怪我嘍). 這樣的話想要實現(xiàn)返回值的重載就只能再寫一個不同名的方法了.

除此之外參數(shù)的順序也會決定函數(shù)的重載, 比如下面的兩個方法:

    private int getHeight(String message, Integer a){
        System.out.println(message + height);
        return height;
    }

    private int getHeight(Integer a, String message){
        return height;
    }

字節(jié)碼為:

public int getHeight(java.lang.String, java.lang.Integer);
    descriptor: (Ljava/lang/String;Ljava/lang/Integer;)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=3
         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_1
         4: aload_0
         5: getfield      #2                  // Field height:I
         8: invokedynamic #4,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
        13: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        16: aload_0
        17: getfield      #2                  // Field height:I
        20: ireturn
      LineNumberTable:
        line 16: 0
        line 17: 16

  public int getHeight(java.lang.Integer, java.lang.String);
    descriptor: (Ljava/lang/Integer;Ljava/lang/String;)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=3
         0: aload_0
         1: getfield      #2                  // Field height:I
         4: ireturn
      LineNumberTable:
        line 21: 0

可以看到, 入參不同順序也會生成兩個不同的方法, 唯一的問題是僅僅是依靠參數(shù)的順序來生成兩個不同的函數(shù)非常容易造成誤解, 一般要避免使用這樣的寫法.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啊终,隨后出現(xiàn)的幾起案子宙址,更是在濱河造成了極大的恐慌渐排,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勉吻,死亡現(xiàn)場離奇詭異鸯屿,居然都是意外死亡,警方通過查閱死者的電腦和手機窃判,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喇闸,“玉大人袄琳,你說我怎么就攤上這事〗鲑耍” “怎么了跨蟹?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵雳殊,是天一觀的道長橘沥。 經常有香客問我,道長夯秃,這世上最難降的妖魔是什么座咆? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮仓洼,結果婚禮上介陶,老公的妹妹穿的比我還像新娘。我一直安慰自己色建,他們只是感情好哺呜,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箕戳,像睡著了一般某残。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陵吸,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天玻墅,我揣著相機與錄音,去河邊找鬼壮虫。 笑死澳厢,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播剩拢,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼线得,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了徐伐?” 一聲冷哼從身側響起框都,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呵晨,沒想到半個月后魏保,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡摸屠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年谓罗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片季二。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡檩咱,死狀恐怖,靈堂內的尸體忽然破棺而出胯舷,到底是詐尸還是另有隱情刻蚯,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布桑嘶,位于F島的核電站炊汹,受9級特大地震影響,放射性物質發(fā)生泄漏逃顶。R本人自食惡果不足惜讨便,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望以政。 院中可真熱鬧霸褒,春花似錦、人聲如沸盈蛮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抖誉。三九已至殊轴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寸五,已是汗流浹背梳凛。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梳杏,地道東北人韧拒。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓淹接,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叛溢。 傳聞我的和親對象是個殘疾皇子塑悼,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法楷掉,內部類的語法厢蒜,繼承相關的語法,異常的語法烹植,線程的語...
    子非魚_t_閱讀 31,660評論 18 399
  • 從三月份找實習到現(xiàn)在斑鸦,面了一些公司,掛了不少草雕,但最終還是拿到小米巷屿、百度、阿里墩虹、京東嘱巾、新浪、CVTE诫钓、樂視家的研發(fā)崗...
    時芥藍閱讀 42,272評論 11 349
  • (401) 我想你旬昭,我的寶 好夢!
    韓尚小閱讀 161評論 1 1
  • 人與人初見的靈光 時常驚呼閬苑仙葩美玉無瑕 仿若一夜間菌湃,仙侶奇緣 須臾问拘,待明日明年 疲倦,之后無關宏旨 一個人熱愛...
    圓善閱讀 247評論 5 8
  • 2017.10.28(農歷九月初九)星期六 晴 提前一天跟孩子爸約好我們重陽節(jié)一起爬山,原因一: 一起完成親子活動...
    育心牛牛媽閱讀 214評論 0 0