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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java中字节码_Java字节码浅析(三)

發(fā)布時間:2025/5/22 java 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中字节码_Java字节码浅析(三) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

英文原文鏈接,譯文鏈接,原文作者:James Bloom,譯者:有孚

從Java7開始,switch語句增加了對String類型的支持。不過字節(jié)碼中的switch指令還是只支持int類型,并沒有增加對其它類型的支持。事實(shí)上switch語句對String的支持是分成兩個步驟來完成的。首先,將每個case語句里的值的hashCode和操作數(shù)棧頂?shù)闹?譯注:也就是switch里面的那個值,這個值會先壓入棧頂)進(jìn)行比較。這個可以通過lookupswitch或者是tableswitch指令來完成。結(jié)果會路由到某個分支上,然后調(diào)用String.equlals來判斷是否確實(shí)匹配。最后根據(jù)equals返回的結(jié)果,再用一個tableswitch指令來路由到具體的case分支上去執(zhí)行。

public int simpleSwitch(String stringOne) {

switch (stringOne) {

case "a":

return 0;

case "b":

return 2;

case "c":

return 3;

default:

return 4;

}

}

這個字符串的switch語句會生成下面的字節(jié)碼:

0: aload_1

1: astore_2

2: iconst_m1

3: istore_3

4: aload_2

5: invokevirtual #2 // Method java/lang/String.hashCode:()I

8: tableswitch {

default: 75

min: 97

max: 99

97: 36

98: 50

99: 64

}

36: aload_2

37: ldc #3 // String a

39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

42: ifeq 75

45: iconst_0

46: istore_3

47: goto 75

50: aload_2

51: ldc #5 // String b

53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

56: ifeq 75

59: iconst_1

60: istore_3

61: goto 75

64: aload_2

65: ldc #6 // String c

67: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

70: ifeq 75

73: iconst_2

74: istore_3

75: iload_3

76: tableswitch {

default: 110

min: 0

max: 2

0: 104

1: 106

2: 108

}

104: iconst_0

105: ireturn

106: iconst_2

107: ireturn

108: iconst_3

109: ireturn

110: iconst_4

111: ireturn

這段字節(jié)碼所在的class文件里面,會包含如下的一個常量池。關(guān)于常量池可以看下JVM內(nèi)部細(xì)節(jié)中的_運(yùn)行時常量池_一節(jié)。

Constant pool:

#2 = Methodref #25.#26 // java/lang/String.hashCode:()I

#3 = String #27 // a

#4 = Methodref #25.#28 // java/lang/String.equals:(Ljava/lang/Object;)Z

#5 = String #29 // b

#6 = String #30 // c

#25 = Class #33 // java/lang/String

#26 = NameAndType #34:#35 // hashCode:()I

#27 = Utf8 a

#28 = NameAndType #36:#37 // equals:(Ljava/lang/Object;)Z

#29 = Utf8 b

#30 = Utf8 c

#33 = Utf8 java/lang/String

#34 = Utf8 hashCode

#35 = Utf8 ()I

#36 = Utf8 equals

#37 = Utf8 (Ljava/lang/Object;)Z

注意,在執(zhí)行這個switch語句的時候,用到了兩個tableswitch指令,同時還有數(shù)個invokevirtual指令,這個是用來調(diào)用String.equals()方法的。在下一篇文章中關(guān)于方法調(diào)用的那節(jié),會詳細(xì)介紹到這個invokevirtual指令。下圖演示了輸入為”b”的情況下,這個swith語句是如何執(zhí)行的。

如果有幾個分支的hashcode是一樣的話,比如說“FB”和”Ea”,它們的hashCode都是28,得簡單的調(diào)整下equals方法的處理流程來進(jìn)行處理。在下面的這個例子中,34行處的字節(jié)碼ifeg 42會跳轉(zhuǎn)到另一個String.equals方法調(diào)用,而不是像前面那樣執(zhí)行l(wèi)ookupswitch指令,因為前面的那個例子中hashCode沒有沖突。(譯注:這里一般容易弄混淆,認(rèn)為ifeq是字符串相等,為什么要跳到下一處繼續(xù)比較字符串?其實(shí)ifeq是判斷棧頂元素是否和0相等,而棧頂?shù)闹稻褪荢tring.equals的返回值,而true,也就是相等,返回的是1,false返回的是0,因此ifeq為真的時候表明返回的是false,這會兒就應(yīng)該繼續(xù)進(jìn)行下一個字符串的比較)

public int simpleSwitch(String stringOne) {

switch (stringOne) {

case "FB":

return 0;

case "Ea":

return 2;

default:

return 4;

}

}

這段代碼會生成下面的字節(jié)碼:

0: aload_1

1: astore_2

2: iconst_m1

3: istore_3

4: aload_2

5: invokevirtual #2 // Method java/lang/String.hashCode:()I

8: lookupswitch {

default: 53

count: 1

2236: 28

}

28: aload_2

29: ldc #3 // String Ea

31: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

34: ifeq 42

37: iconst_1

38: istore_3

39: goto 53

42: aload_2

43: ldc #5 // String FB

45: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

48: ifeq 53

51: iconst_0

52: istore_3

53: iload_3

54: lookupswitch {

default: 84

count: 2

0: 80

1: 82

}

80: iconst_0

81: ireturn

82: iconst_2

83: ireturn

84: iconst_4

85: ireturn

###循環(huán)語句

if-else和switch這些條件流程控制語句都是先通過一條指令比較兩個值,然后跳轉(zhuǎn)到某個分支去執(zhí)行。

for循環(huán)和while循環(huán)這些語句也類似,只不過它們通常都包含一個goto指令,使得字節(jié)碼能夠循環(huán)執(zhí)行。do-while循環(huán)則不需要goto指令,因為它們的條件判斷指令是放在循環(huán)體的最后來執(zhí)行。

有一些操作碼能在單條指令內(nèi)完成整數(shù)或者引用的比較,然后根據(jù)結(jié)果跳轉(zhuǎn)到某個分支繼續(xù)執(zhí)行。而比較double,long,float這些類型則需要兩條指令。首先會將兩個值進(jìn)行比較,然后根據(jù)結(jié)果把1,-1,0壓入操作數(shù)棧中。然后再根據(jù)棧頂?shù)闹凳谴笥谛∮诨蛘叩扔?,來決定下一步要執(zhí)行的指令的位置。這些指令在上一篇文章中有詳細(xì)的介紹。

####while循環(huán)

while循環(huán)包含條件跳轉(zhuǎn)指令比如if_icmpge 或者if_icmplt(前面有介紹)以及goto指令。如果判斷條件不滿足的話,會跳轉(zhuǎn)到循環(huán)體后的第一條指令繼續(xù)執(zhí)行,循環(huán)結(jié)束(譯注:這里判斷條件和代碼中的正好相反,如代碼中是i<2,字節(jié)碼內(nèi)是i>=2,從字節(jié)碼的角度看,是滿足條件后循環(huán)中止)。循環(huán)體的末尾是一條goto指令,它會跳轉(zhuǎn)到循環(huán)開始的地方繼續(xù)執(zhí)行,直到分支跳轉(zhuǎn)的條件滿足才終止。

public void whileLoop() {

int i = 0;

while (i < 2) {

i++;

}

}

編譯完后是:

0: iconst_0

1: istore_1

2: iload_1

3: iconst_2

4: if_icmpge 13

7: iinc 1, 1

10: goto 2

13: return

if_icmpge指令會判斷局部變量區(qū)中的1號位的變量(也就是i,譯注:局部變量區(qū)從0開始計數(shù),第0位是this)是否大于等于2,如果不是繼續(xù)執(zhí)行,如果是的話跳轉(zhuǎn)到13行處,結(jié)束循環(huán)。goto指令使得循環(huán)可以繼續(xù)執(zhí)行,直到條件判斷為真,這個時候會跳轉(zhuǎn)到緊挨著循環(huán)體后邊的return指令處。iinc是少數(shù)的幾條能直接更新局部變量區(qū)里的變量的指令之一,它不用把值壓到操作數(shù)棧里面就能直接進(jìn)行操作。這里iinc指令把第1個局部變量(譯注:第0個是this)自增1。

for循環(huán)和while循環(huán)在字節(jié)碼里的格式是一樣的。這并不奇怪,因為每個while循環(huán)都可以很容易改寫成一個for循環(huán)。比如上面的while循環(huán)就可以改寫成下面的for循環(huán),當(dāng)然了它們輸出的字節(jié)碼也是一樣的:

public void forLoop() {

for(int i = 0; i < 2; i++) {

}

}

####do-while循環(huán)

do-while循環(huán)和for循環(huán),while循環(huán)非常類似,除了一點(diǎn),它是不需要goto指令的,因為條件跳轉(zhuǎn)指令在循環(huán)體的末尾,可以用它來跳轉(zhuǎn)回循環(huán)體的起始處。

public void doWhileLoop() {

int i = 0;

do {

i++;

} while (i < 2);

}

這會生成如下的字節(jié)碼:

0: iconst_0

1: istore_1

2: iinc 1, 1

5: iload_1

6: iconst_2

7: if_icmplt 2

10: return

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的java中字节码_Java字节码浅析(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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