Java溫故知新 - Switch語句



一、基本用法

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 }

生成的字節碼始終為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 }

從字節碼可以看出,對於枚舉類型也是調用它的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


當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

由此可以看出,這兩種編譯結果是編譯器在考慮空間占用情況下,對代碼效率進行的優化。

另外需要注意一點,就是不管是tableswitch還是lookupswitch,default標簽都放在最后進行匹配,
但下面各個case和default語句塊的順序是與源代碼相同的。所以盡管default標簽沒有放在最后,
但它仍然是最后被匹配的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM