一、基本用法
switch的case語句可以處理int,short,byte,char類型的值,但是不能處理long,String等類型。
因為short,byte,char都會轉換成int進行處理,這一點也可以從生成的字節碼看出。
char a = 'e'; switch (a) { case 'c': System.out.println("In case c"); break; case 'd': System.out.println("In case d"); break; default: System.out.println("In default"); break; case 'e': System.out.println("In case e"); break; }
0:
bipush
101
2: istore_1
3: iload_1
4: tableswitch{ //99 to 101
99: 32;
100: 43;
101: 65;
default: 54 }
2: istore_1
3: iload_1
4: tableswitch{ //99 to 101
99: 32;
100: 43;
101: 65;
default: 54 }
生成的字節碼始終為bipush或iconst(小於5的整數),用於將整數壓入棧。
二、新特性
在JDK 5中加入的枚舉Enum類型也是可以作為case值的。
Type t = Type.C; switch (t) { case A: System.out.println("In case A"); break; case B: System.out.println("In case B"); break; default: System.out.println("In default"); break; case C: System.out.println("In case C"); break; }
0: getstatic #18; //Field com/cdai/jdk/Type.C:Lcom/cdai/jdk/Type;
3: astore_1
4: invokestatic #24; //Method $SWITCH_TABLE$com$cdai$jdk$Type:()[I
7: aload_1
8: invokevirtual #27; //Method com/cdai/jdk/Type.ordinal:()I
11: iaload
12: tableswitch{ //1 to 3
1: 40;
2: 51;
3: 73;
default: 62 }
3: astore_1
4: invokestatic #24; //Method $SWITCH_TABLE$com$cdai$jdk$Type:()[I
7: aload_1
8: invokevirtual #27; //Method com/cdai/jdk/Type.ordinal:()I
11: iaload
12: tableswitch{ //1 to 3
1: 40;
2: 51;
3: 73;
default: 62 }
從字節碼可以看出,對於枚舉類型也是調用它的ordinal方法轉成整型后進行switch匹配的。
在JDK 7中,又加入了對String類型的支持,從此不用再寫If-Else來判斷字符串了。
三、兩種字節碼
switch語句有兩種編譯結果:
當case中的值連續時,編譯成tableswitch,解釋執行時從table數組根據case值計算下標來取值,從數組中取到的
便是要跳轉的行數。
int a = 1; switch (a) { case 2: System.out.println("In case 2"); break; case 3: System.out.println("In case 3"); break; default: System.out.println("In default"); break; case 1: System.out.println("In case 1"); break; }
0: iconst_1
1: istore_1
2: iload_1
3: tableswitch{ //1 to 3
1: 61;
2: 28;
3: 39;
default: 50 }
28: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #22; //String In case 2
33: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: goto 69
39: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #30; //String In case 3
44: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: goto 69
50: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
53: ldc #32; //String In default
55: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: goto 69
61: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
64: ldc #34; //String In case 1
66: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
1: istore_1
2: iload_1
3: tableswitch{ //1 to 3
1: 61;
2: 28;
3: 39;
default: 50 }
28: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #22; //String In case 2
33: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: goto 69
39: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #30; //String In case 3
44: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: goto 69
50: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
53: ldc #32; //String In default
55: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: goto 69
61: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
64: ldc #34; //String In case 1
66: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
當case中的值不連續時,編譯成lookupswitch,解釋執行時需要從頭到尾遍歷找到case對應的代碼行。
因為case對應的值不是連續的,如果仍然用表來保存case對應的行號,會浪費大量空間。
int a = 10; switch (a) { case 2: System.out.println("In case 2"); break; case 3: System.out.println("In case 3"); break; default: System.out.println("In default"); break; case 10: System.out.println("In case 10"); break; }
0: bipush 10
2: istore_1
3: iload_1
4: lookupswitch{ //3
2: 40;
3: 51;
10: 73;
default: 62 }
40: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
43: ldc #22; //String In case 2
45: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: goto 81
51: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
54: ldc #30; //String In case 3
56: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: goto 81
62: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
65: ldc #32; //String In default
67: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
70: goto 81
73: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
76: ldc #34; //String In case 10
78: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
2: istore_1
3: iload_1
4: lookupswitch{ //3
2: 40;
3: 51;
10: 73;
default: 62 }
40: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
43: ldc #22; //String In case 2
45: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: goto 81
51: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
54: ldc #30; //String In case 3
56: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: goto 81
62: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
65: ldc #32; //String In default
67: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
70: goto 81
73: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
76: ldc #34; //String In case 10
78: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
由此可以看出,這兩種編譯結果是編譯器在考慮空間占用情況下,對代碼效率進行的優化。
另外需要注意一點,就是不管是tableswitch還是lookupswitch,default標簽都放在最后進行匹配,
但下面各個case和default語句塊的順序是與源代碼相同的。所以盡管default標簽沒有放在最后,
但它仍然是最后被匹配的。