java的流程控制語句中,選擇判斷語句有兩種if...else和switch。相對而言,switch在實際使用過程中需要注意的地方較多,有時會由於忘記它的一些語法特征,對其語法產生誤解,從而導致一些錯誤。這里通過查閱資料和編碼實踐對switch做出一些小結。
一、switch的基本語法
switch的基本語法結構為
switch (表達式){ case 常量1: // 代碼塊1; break; case 常量2: // 代碼塊2; break; default: // 代碼塊n; break; }
switch條件判斷語句涉及四個關鍵字:switch、case、default、break
switch:表示條件表達式,括號里的值是一個規定數據類型的值。
case:表示條件分支,case后面跟一個常量,每個case后面的值必須不一樣。case的個數可以為0個。
default:表示默認分支,default可以省略,通常放在最后。
break:表示“停止”,跳出當前switch語句。
二、switch支持的數據類型
switch()括號中的表達式的結果的類型只能是特定類型,我們將一個double類型的變量放入,發現編譯報錯:Cannot switch on a value of type double. Only convertible int values, strings or enum variables are permitted。
不難看出這里括號中只支持是int,String,enum型的。
由於java中的類型的自動轉型,byte、char、short這三種可以自動轉換為int型的類型括號中也支持。
由於java中包裝類的自動拆箱,Integer、Byte、Char、Short這四種類型括號中也支持。
總結來說:switch()括號中的表達式支持int、String、enum以及可以自動轉型為int的其他類型。
注意:在Java1.6中表達式的類型只能為int和enum,在java1.7后支持了對String的判斷,String類型比較特殊,后續會講到。
三、switch的執行順序
switch的執行順序如下:
1.先計算並獲得switch后面小括號里的表達式或變量的值,然后將計算結果按照代碼順序與每個case后的常量比較。當二者相等時,執行這個case塊中的代碼塊;若case后的常量與該值都不相等且default存在,則執行default中個代碼塊;若若case后的常量與該值都不相等且default不存在,則switch語句執行結束
2.當開始執行一個條件分支的代碼(case中的常量與switch后的結果相等,或進入default后的代碼塊),會從該代碼塊開始,跳過剩余的條件判斷,從上至下執行代碼塊,直到遇到break或執switch中之后的代碼塊全部執行完畢。例子如下:
// 例一 int i = 1; switch ( i ) { case 1: System.out.println(1); case 2: System.out.println(2); default : System.out.println("default"); case 3: System.out.println(3); break; case 4: System.out.println(4); break; } // 例一執行結果 1 2 default 3 // 例二 int i = 5; switch ( 5 ) { case 1: System.out.println(1); break; case 2: System.out.println(2); break; default : System.out.println("default"); case 3: System.out.println(3); break; case 4: System.out.println(4); break; } // 例二執行結果 default 3
四、switch使用中的注意事項
1.case后面必須跟常量,如果是變量,必須是final修飾的在編譯時可識別的一般類型變量或字符串(包括包裝類在內的其他引用類型都不可以),本質上也是常量。
如:final int a1 = 1; case a1; // 正確 final int a2; case a2; // 異常 The local variable i may not have been initialized final Integer = 3; case a3; // 異常 case expressions must be constant expressions final String a4 = "hello"; case a4; // 正確
2.switch在進入某條件分支后(case或default)會一直往下執行代碼,直到遇到break。這點上面有例子講到。
3.switch使用枚舉進行選擇判斷時,在switch()的括號中要指定枚舉類名.枚舉常量名,case后直接接該枚舉類的枚舉常量名。
enum Season{ SPRING, SUMMER, AUTUMN, WINTER; } public static void main(String[] args) { Season season = Season.SPRING ; switch ( season ) { case SPRING: System.out.println("spring"); break; case SUMMER: System.out.println("summer"); break; case AUTUMN: System.out.println("antumn"); break; case WINTER: System.out.println("winter"); break; } }
編譯后實際上switch()括號中的值,case后接的值,與對應枚舉常量在枚舉類中的序數有關,從代碼看是序數+1,具體實現原理這里看不出來。反編譯后的代碼如下,不同環境下反編譯結果不一樣,但可以看出原理是一樣的。
Season season = Season.SPRING; switch ($SWITCH_TABLE$com$test$processControl$SwitchTest$Season()[season.ordinal()]) { case 1: System.out.println("spring"); break; case 2: System.out.println("summer"); break; case 3: System.out.println("antumn"); break; case 4: System.out.println("winter"); } }
4.switch使用String進行選擇判斷時,編譯后實際上switch()括號中的值,case后接的值都是對應字符串的哈希值,且判斷哈希值后會再次用equls()方法判斷是否是同一個字符串。
String str = "a"; String str1; switch ((str1 = str).hashCode()) { case 98: if (str1.equals("b")); break; case 99: if (!(str1.equals("c"))) { break label82: System.out.println("b"); } else { System.out.println("c"); } break; default: label82: System.out.println("default"); }
不通環境下反編譯結果不一,為方便理解,我們可以將上述代碼理解為以下邏輯
String str = "a"; String str1 = str; switch ( str1.hashCode() ){ case 98: // "b".hashCode(); if( str1.equals("b") ){ System.out.println("b"); } break; case 99: // "c".hashCode(); if ( str1.equals("c") ) { System.out.println("c"); } break; default: System.out.println("default"); }