不加break會怎樣
觀察下面的代碼:
第一段代碼是最常見的寫法,也是最好的寫法,約定俗成的每個條件語句後添加break。如果因爲某種原因沒有寫break語句,沒有對此情況進行過探究的話,可能還真不知道第二、三段代碼會輸出什麼。
結論:如果不加break,程序從匹配成功的case語句開始,一直到遇見break語句或者執行完成所以條件語句塊後纔會跳出判斷,不管後面的case語句是否匹配成功都會執行語句塊中的代碼,也就是上面的第二、三段代碼。
有時候會故意缺省break語句來達到某種效果(例如幾種判斷都執行相同的代碼塊),不過這種情況極少,比如下面這個並沒有太大意義的例子,當檢測到當前月份是1、2、3、4月時都會輸出“現在是第一季度”:
switch (month){
case 1:
case 2:
case 3:
case 4:
System.out.println("現在是第一季度");
break;
default:
break;
}
switch語句的原理
根據經驗,當switch語句和if…else if…else語句可以實現相同功能(並且分支較多)時會盡量使用switch語句,因爲switch語句的效率更高。
if…else if…else語句的執行方式是順序執行所有判斷語句,直到滿足條件時執行語句代碼塊。
switch語句會維護一張跳轉表,不管case判斷語句的值是不是按照順序的,內存中的地址表都會按照順序進行排列。
switch語句會生成一個跳轉表來指示實際的case分支的地址,而這個跳轉表的索引號與switch變量的值是相等的。從而,switch-case不用像if-else if那樣遍歷條件分支直到命中條件,而只需訪問對應索引號的表項從而到達定位分支的目的。 具體地說,switch-case會生成一份表項數爲case量+1的跳錶,程序首先判斷switch變量是否大於(小於)最大(最小)case 常量,若大於(小於),則跳到default分支處理;否則取得索引號爲switch變量大小的跳錶項的地址(即跳錶的起始地址+表項大小*索引號),程序接着跳到此地址執行,到此完成了分支的跳轉。
case判斷的條件會有下面幾種情況,對應switch語句中的執行過程是不一樣的:
1、case條件的值是連續的,例如:case 1、case 2、case 3、case 4、case 5
在switch中,編譯器多增加了一個數組用於存儲每個case對應的地址,根據switch中傳入的整數在數組中查到到對應的地址,直接通過這個地址跳轉到對應的位置。編譯器在處理switch時會首先校驗不滿足所有case的情況,當這種情況發生時代碼調轉到default或者switch語句塊之外。然後將傳入的整數值減一(數組元素是從0開始計數)。最後根據參數值找到應該跳轉的位置。
2、case條件的值不是連續的,例如:case 1、case 2、case 3、case 5
去除掉上面的case 4條件後,仍會建立一個表,對應下圖中的關係。
3、case條件的值不是連續的,例如:case 1、case 2、case 5、case 6、case 256
如果case條件間的值差異較大,則編譯器會採用索引表的方式來進行地址的跳轉,對應下圖中的關係。
如果想對內存進行深入研究的話可以編寫demo對內存進行跟蹤,下面這兩篇文章描述的也比較詳細:https://www.cnblogs.com/mukekeheart/p/10558167.html、https://www.jianshu.com/p/d382f653dd00
性能優化
- 因爲if…else if…else語句會逐個條件進行判斷,直到命中,所以應將機率大的條件置於前面,這樣可以減少比較的次數
- 如果分支較少,比如小於4個,或者case語句判斷的值跳躍較大時,沒必要使用switch語句
- 編譯器是根據時間和空間的消耗來決定那種方式效率更高,所以在Switch寫判斷條件的時候最好做到連續緊密,可以最大限度的節省時間和內存
switch語句的注意事項
- 當遇到 break 語句時,switch 纔會終止,控制流將跳轉到 switch 語句後的下一行
- Java中的switch語句中表達式的值必須是
整型、字符型、字符串類型(JDK7添加的新特性)和枚舉類型
引申:case語句後常量表達式的值絕不可以是實數(有限小數、無限小數),例如case 1.1:
即爲不合法語句 - C++中的switch語句中表達式的值必須是整型、字符型和枚舉類型,
不能是字符串類型
- switch語句與其他控制語句的一個不同點:每個case中的多條語句加不加大括號均可
- 最好在switch中添加default語句,用來處理異常情況