为什么 Java 中的 switch case 语句只接受整数、短字节、字节和字符,而不接受其他数据类型?有什么好处?请详细说明。
2 回答
通常语言设计问题归结为“因为这是设计师决定这样做的方式”。这只是另一个时代。
但是Java有一些起源于C,它做同样的事情,在80年代,这个决定被解释为因为编译器可以把开关变成一个跳转表:基本上,每个代码块的地址都放在一个表,然后switch
变成范围检查,然后使用您传入的值进行表查找(通常索引到数组或至少是数组的链表)以获取地址,然后跳转到该地址。在这种情况下,只有整数才有意义。请记住,计算机并不总是像现在这样快。C 是在 70 年代初期基于 60 年代后期的工作设计的,当时计算机速度要慢得多。
与 Java 和 C具有相同句法传统的一些语言,例如 JavaScript,switch
只是另一种编写方式,if...else/if...else
并且不将检查的类型限制为整数类型,也许是因为在 90 年代设计的,这成为了一个现实的选择. 或者可能只是因为 JavaScript 的设计者(Brendan Eich)更喜欢这种方式。
下面,Baadshah问道:
出于好奇:那么现在它如何支持字符串?你能给出一些想法吗?
首先,让我们退一步来看一下int
案例:
num = Integer.parseInt(args[0]);
switch (num) {
case 1:
System.out.println("You used the special value one");
break;
case 42:
System.out.println("You used the special value forty-two");
break;
case 67:
System.out.println("You used the special value sixty-seven");
break;
default:
System.out.println("You used the a non-special value " + num);
break;
}
这会产生这样的字节码:
19:iload_2 20: 查找开关 { // 3 1:56 42:67 67:78 默认值:89 } 56: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 59: ldc #9 // String 你使用了特殊值 one 61: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 64:转到 114 67: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 70: ldc #11 // String 你使用了特殊值 42 72: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 75:转到 114 78: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 81: ldc #12 // String 你使用了特殊值 67 83: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 86:转到 114 89: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 92:新 #13 // 类 java/lang/StringBuilder 95:重复 96: invokespecial #14 // 方法 java/lang/StringBuilder."":()V 99: ldc #15 // String 你使用了非特殊值 101: invokevirtual #16 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 104:iload_2 105: invokevirtual #17 // 方法 java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 108: invokevirtual #18 // 方法 java/lang/StringBuilder.toString:()Ljava/lang/String; 111: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V
我们可以在实际中看到表查找int
。
那么你如何用字符串做到这一点?嗯,一个答案就是把它switch
变成一个if...else if...else
结构。但他们做了比这更聪明的事情:他们使用哈希码进行优化,然后用于equals
防止冲突:
switch (str) {
case "abc":
System.out.println("You used the special value 'abc'");
break;
case "def":
System.out.println("You used the special value 'def'");
break;
case "ghi":
System.out.println("You used the special value 'ghi'");
break;
default:
System.out.println("You used the a non-special value '" + str + "'");
break;
}
变成:
124:加载 4 126: invokevirtual #19 // 方法 java/lang/String.hashCode:()I 129: 查找开关 { // 3 96354:164 99333:180 102312:196 默认:209 } 164:加载 4 166: ldc #20 // 字符串 abc 168: invokevirtual #21 // 方法 java/lang/String.equals:(Ljava/lang/Object;)Z 171:ifeq 209 174:图标st_0 175:istore 5 177:转到209 180:加载 4 182: ldc #22 // 字符串定义 184: invokevirtual #21 // 方法 java/lang/String.equals:(Ljava/lang/Object;)Z 187:ifeq 209 190:图标st_1 191:istore 5 193:转到209 196:加载 4 198: ldc #23 // 字符串 ghi 200: invokevirtual #21 // 方法 java/lang/String.equals:(Ljava/lang/Object;)Z 203:ifeq 209 206:图标st_2 207:istore 5 209:加载 5 211: tableswitch { // 0 到 2 0:236 1:247 2:258 默认值:269 } 236: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 239: ldc #24 // String 你使用了特殊值'abc' 241: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 244:转到299 247: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 250: ldc #25 // String 你使用了特殊值'def' 252: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 255:转到299 258: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 261: ldc #26 // String 你使用了特殊值'ghi' 263: invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 266:转到299 269: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 272:新的 #13 // 类 java/lang/StringBuilder 275:重复 276: invokespecial #14 // 方法 java/lang/StringBuilder."":()V 279: ldc #27 // String You used the a non-special value ' 281: invokevirtual #16 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 284:加载_3 285: invokevirtual #16 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 288: ldc #28 // 字符串' 290: invokevirtual #16 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 293:invokevirtual #18 // 方法 java/lang/StringBuilder.toString:()Ljava/lang/String; 296:invokevirtual #10 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V
看看他们在那里做了什么?现在基本上是两个switches
:一个根据哈希码为每个案例获取一个唯一编号(但使用 进行双重检查equals
),然后第二个发送。
JDK6 switch 语句适用于 char、byte、int 原始数据类型和枚举。在 JDK 7 中,他们意识到 java.lang.String 也是一个常量,并已添加到 switch 语句支持的数据类型列表中。
例如,以下代码在 JDK7 中运行良好。
public static void OpenSource(String language)
{
switch (language) {
case "PERL":
System.out.println("PERL");
break;
case "Python":
System.out.println("Python");
break;
case "Ruby":
System.out.println("Ruby");
break;
case "PHP":
System.out.println("PHP");
break;
default:
throw new IllegalArgumentException();
}
}