Java 官方文檔描述:
A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types, the String class, and a few special classes that wrap certain primitive types: Character, Byte, Short, and Integer
在基本數據類型 byte / char 下
public class SwitchBasicTest {
private int switchByte(byte i) {
int ret;
switch (i) {
case 0:
ret = 10;
break;
case 1:
ret = 11;
break;
default:
ret = 0;
}
return ret;
}
private int switchChar(char i) {
int ret;
switch (i) {
case 0:
ret = 10;
break;
case 1:
ret = 11;
break;
case 'a':
ret = 20;
break;
case '寫':
ret = 21;
break;
default:
ret = 0;
}
return ret;
}
}
用 javap -v 或其他工具反編譯 SwitchBasicTest.class
,本文使用 IDEA 插件 jclasslib Bytecode viewer
反編譯得到 switchByte
方法的字節碼:
0 iload_1
1 lookupswitch 2
0: 28 (+27)
1: 34 (+33)
default: 40 (+39)
28 bipush 10
30 istore_2
31 goto 42 (+11)
34 bipush 11
36 istore_2
37 goto 42 (+5)
40 iconst_0
41 istore_2
42 iload_2
43 ireturn
switchChar
方法的字節碼:
0 iload_1
1 lookupswitch 4
0: 44 (+43)
1: 50 (+49)
97: 56 (+55)
20889: 62 (+61)
default: 68 (+67)
44 bipush 10
46 istore_2
47 goto 70 (+23)
50 bipush 11
52 istore_2
53 goto 70 (+17)
56 bipush 20
58 istore_2
59 goto 70 (+11)
62 bipush 21
64 istore_2
65 goto 70 (+5)
68 iconst_0
69 istore_2
70 iload_2
71 ireturn
可知對于 byte
等基礎數據類型,switch
直接使用整型做比較
- byte, short 可直接轉整整值
- char 取
unicode
整型碼值 - Integer 等包裝類型,會調用
intValue
方法獲得變量的整值
作用在 String 下(JDK1.7及之后版本)
public class SwitchBasicTest {
private int switchString(String i) {
int ret;
switch (i) {
case "0":
ret = 10;
break;
case "1":
ret = 11;
break;
case "a":
ret = 20;
break;
default:
ret = 0;
}
return ret;
}
}
使用 IDEA 編輯器打開 .class 文件看到反編譯代碼:
public class SwitchBasicTest {
private int switchString(String i) {
byte var4 = -1;
switch(i.hashCode()) {
case 48:
if (i.equals("0")) {
var4 = 0;
}
break;
case 49:
if (i.equals("1")) {
var4 = 1;
}
break;
case 97:
if (i.equals("a")) {
var4 = 2;
}
}
byte ret;
switch(var4) {
case 0:
ret = 10;
break;
case 1:
ret = 11;
break;
case 2:
ret = 20;
break;
default:
ret = 0;
}
return ret;
}
}
反編譯得到 switchString
的字節碼:
0 aload_1
1 astore_3
2 iconst_m1
3 istore 4
5 aload_3
6 invokevirtual #8 <java/lang/String.hashCode>
9 lookupswitch 3
48: 44 (+35)
49: 59 (+50)
97: 74 (+65)
default: 86 (+77)
44 aload_3
45 ldc #9 <0>
47 invokevirtual #10 <java/lang/String.equals>
50 ifeq 86 (+36)
53 iconst_0
54 istore 4
56 goto 86 (+30)
59 aload_3
60 ldc #11 <1>
62 invokevirtual #10 <java/lang/String.equals>
65 ifeq 86 (+21)
68 iconst_1
69 istore 4
71 goto 86 (+15)
74 aload_3
75 ldc #12 <a>
77 invokevirtual #10 <java/lang/String.equals>
80 ifeq 86 (+6)
83 iconst_2
84 istore 4
86 iload 4
88 tableswitch 0 to 2
0: 116 (+28)
1: 122 (+34)
2: 128 (+40)
default: 134 (+46)
116 bipush 10
118 istore_2
119 goto 136 (+17)
122 bipush 11
124 istore_2
125 goto 136 (+11)
128 bipush 20
130 istore_2
131 goto 136 (+5)
134 iconst_0
135 istore_2
136 iload_2
137 ireturn
可以看到 switch
作用在 String
上最終拆分成兩個針對整型 switch
語句,具體流程為:
- 計算并比較 hashcode
- 如果 hashcode 相同,則進一步使用
equals
確定字符串內容相同,處理兩個字符串 hashcode 相同的情況
作用在枚舉下
public enum SeasonEnum {
SPRING, SUMMER, AUTUMN, WINTER
}
public class SwitchEnumTest {
private static int testSpring(SeasonEnum seasonEnum) {
int ret;
switch (seasonEnum) {
case AUTUMN:
ret = 1;
break;
case SPRING:
ret = 2;
break;
case SUMMER:
ret = 3;
break;
case WINTER:
ret = 4;
break;
default:
ret = 5;
}
return ret;
}
}
反編譯得到 testSpring
方法的字節碼:
0 getstatic #6 <xx/SwitchEnumTest$1.$SwitchMap$xx$enumt$SeasonEnum>
3 aload_0
4 invokevirtual #7 <xx/enumt/SeasonEnum.ordinal>
7 iaload
8 tableswitch 1 to 4 1: 40 (+32)
2: 45 (+37)
3: 50 (+42)
4: 55 (+47)
default: 60 (+52)
40 iconst_1
41 istore_1
42 goto 62 (+20)
45 iconst_2
46 istore_1
47 goto 62 (+15)
50 iconst_3
51 istore_1
52 goto 62 (+10)
55 iconst_4
56 istore_1
57 goto 62 (+5)
60 iconst_5
61 istore_1
62 iload_1
63 ireturn
可以看到:
4 invokevirtual #7 <xx/enumt/SeasonEnum.ordinal>
即 switch
作用在枚舉上時,調用枚舉的 ordinal
方法 ,即最終還是 int
值比較
lookupswitch 和 tableswitch
-
tableswitch
使用數組數據結構,通過下標可直接定位到跳轉目標行 -
lookupswitch
維護了一個key-value的關系,通過逐個比較索引來查找匹配的待跳轉的行數 -
tableswitch
比lookupswitch
查找性能更佳 -
switch
語句中的case
分支條件值比較稀疏時,tableswitch
指令的空間使用率偏低,這種情況下可能使用lookupswitch
指令
結論
-
switch
語法參數實際上只對整型有效 -
break
在字節碼層面上會生成一條goto
語句,case
下不帶break
時直接進入下一個case
邏輯 -
switch
作用在枚舉上實際使用了枚舉的ordinal
方法返回值作為比較的依據 -
switch
作用在字符串上時使用字符串的hashcode
作為比較的依據,hashcode
相同時進一步使用equals
比較字符串內容 - 根據java虛擬機規范,java虛擬機的
tableswitch
和lookupswitch
指令都只能支持int類型的條件值,所以switch
不支持long
長整型