java 在 switch 语句这一项上更多的是承袭了 c++,其实整个语法就是 c 风格的。java 的 switch 里只能用 byte、char、short、int 和 enum 类型,连 long 型都不能用(因为 switch 里的要被转换为 int 型,而 long 太长了)。要说不支持 float 和 double 那样的浮点那好理解,因为它们本身是不精确的,1 可能是 0.9999999999。boolean 就两值,放 switch 里无意义,因为 c++ 的 switch 里可用bool 型,但在 java 中遭摈弃。
现在在 jdk7 里 switch 可以用字符串了,学了 c#,字符串的好处就是它是表意的,像 switch(action) case "create" : ...; case "delete" : .....,多写意啊,再也不用先定义一堆常量,然后再 switch(action) case Constants.ACTION_CREATE : ......; case Constants.ACTION_DELETE : ......,当然写过不少程序的尽量会避免写成 case 1... case 2 这样的写法了,会让人不知所以的。常量放在 case 中就要求我们经常要去查它定义的值是什么。
虽然 switch 里可以用字符串了,我想还是应该谨慎些,别搞得以后不易查错,看看:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
						 public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {      String typeOfDay;      switch (dayOfWeekArg) {          case "Monday":              typeOfDay = "Start of work week";              break;          case "Tuesday":          case "Wednesday":          case "Thursday":              typeOfDay = "Midweek";              break;          case "Friday":              typeOfDay = "End of work week";              break;          case "Saturday":          case "Sunday":              typeOfDay = "Weekend";              break;          default:              throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);      }      return typeOfDay; }  | 
					
原文中只是说字符串吗,会使用 equals() 方法来比较,然后决定程序的分支走向,并且字符串是区分大小写的,java 编译器对 switch 生成的字节码执行效率上要高于 if-then-else 语句。
为了加深对 switch 中引入字符串支持的理解,我们再进一步,用例子来看看到底发生了什么,java 代码如下:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  | 
						/**  * jdk 7 switch 中使用字符串测试程序  * @author Unmi  */ public class Test {     private static int c = 0;     public static void main(String[] args){         int i = 21;         switch(i){             case 3: bar();             case 4: bar();             default:bar();         }     }     public void foo(){         String a = "";         switch(a){             case "3": bar();             case "4": bar();             default: bar();         }     }     public static void bar(){     } }  | 
					
由它相应的字节码来帮助理解:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72  | 
						Compiled from "Test.java" public class Test extends java.lang.Object {   public Test();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."":()V        4: return           public static void main(java.lang.String[]);     Code:        0: bipush        21        2: istore_1        3: iload_1        4: lookupswitch  { // 2                      3: 32                      4: 35                default: 38           }       32: invokestatic  #2                  // Method bar:()V       35: invokestatic  #2                  // Method bar:()V       38: invokestatic  #2                  // Method bar:()V       41: return           public void foo();     Code:        0: ldc           #3                  // String        2: astore_1        3: aload_1        4: astore_2        5: iconst_m1        6: istore_3        7: aload_2        8: invokevirtual #4                  // Method java/lang/String.hashCode:()I       11: lookupswitch  { // 2                     51: 36                     52: 50                default: 61           }       36: aload_2       37: ldc           #5                  // String 3       39: invokevirtual #6                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z       42: ifeq          61       45: iconst_0       46: istore_3       47: goto          61       50: aload_2       51: ldc           #7                  // String 4       53: invokevirtual #6                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z       56: ifeq          61       59: iconst_1       60: istore_3       61: iload_3       62: lookupswitch  { // 2                      0: 88                      1: 91                default: 94           }       88: invokestatic  #2                  // Method bar:()V       91: invokestatic  #2                  // Method bar:()V       94: invokestatic  #2                  // Method bar:()V       97: return           public static void bar();     Code:        0: return           static {};     Code:        0: iconst_0        1: putstatic     #8                  // Field c:I        4: return }  | 
					
在 main() 方法中的 switch 语句,判断的是 int 型,与原来的处理办法是一样,是什么值就跳到哪个指令,只会生成一个 lookupswitch 指令。
而在 foo() 方法中的 switch 语句,判断的是字符串,我们看到启用了两条 lookupswitch 指令来支持这一特性,怎么理解它呢?分两步走,从字节码来看似乎有些画蛇添足。
首先它把 case 中的字符串用 hashCode() 算了一些,生成一条 lookupswitch 指令,就是上面的:
  8: invokevirtual #4                  // Method java/lang/String.hashCode:()I
 11: lookupswitch  { // 2
               51: 36
               52: 50
          default: 61
     }
"3" 和 "4" 的 hashCode 分别是 51 和 52,其实这里应该可以像对待整数一样看待字符串,只需要全部转换为相应的 hashCode 即可。不过上面的字令没有那么直接,而是根据传入的 switch 参数,通过 equase() 方法来判断,对原来的 switch 语句重新设定基于 0 开始的整数为参数,也就是对原来的每一个 case 字符串全部转换为整数 0、1、2 ......,最后全部进入到新的 lookupswitch 语句中,从而实现字符串的 switch 功能。
为什么不直接 hashCode 一步完成了,也许这样的效率会比较高吧。
参考:1. http://download.java.net/jdk7/docs/technotes/guides/language/strings-switch.html
本文链接 https://yanbin.blog/jdk-7-enhance-strings-witch/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 
 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。