JDK 7 中的语法增强 -- (3) swith 里用 String 类型
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 里可以用字符串了,我想还是应该谨慎些,别搞得以后不易查错,看看:
为了加深对 switch 中引入字符串支持的理解,我们再进一步,用例子来看看到底发生了什么,java 代码如下:
由它相应的字节码来帮助理解:
在 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's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
现在在 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 public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
2 String typeOfDay;
3 switch (dayOfWeekArg) {
4 case "Monday":
5 typeOfDay = "Start of work week";
6 break;
7 case "Tuesday":
8 case "Wednesday":
9 case "Thursday":
10 typeOfDay = "Midweek";
11 break;
12 case "Friday":
13 typeOfDay = "End of work week";
14 break;
15 case "Saturday":
16 case "Sunday":
17 typeOfDay = "Weekend";
18 break;
19 default:
20 throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
21 }
22 return typeOfDay;
23}
原文中只是说字符串吗,会使用 equals() 方法来比较,然后决定程序的分支走向,并且字符串是区分大小写的,java 编译器对 switch 生成的字节码执行效率上要高于 if-then-else 语句。为了加深对 switch 中引入字符串支持的理解,我们再进一步,用例子来看看到底发生了什么,java 代码如下:
1/**
2 * jdk 7 switch 中使用字符串测试程序
3 * @author Unmi
4 */
5public class Test {
6
7 private static int c = 0;
8
9 public static void main(String[] args){
10 int i = 21;
11 switch(i){
12 case 3: bar();
13 case 4: bar();
14 default:bar();
15 }
16 }
17
18 public void foo(){
19 String a = "";
20 switch(a){
21 case "3": bar();
22 case "4": bar();
23 default: bar();
24 }
25 }
26
27 public static void bar(){
28 }
29} 1Compiled from "Test.java"
2public class Test extends java.lang.Object {
3 public Test();
4 Code:
5 0: aload_0
6 1: invokespecial #1 // Method java/lang/Object."":()V
7 4: return
8
9 public static void main(java.lang.String[]);
10 Code:
11 0: bipush 21
12 2: istore_1
13 3: iload_1
14 4: lookupswitch { // 2
15 3: 32
16 4: 35
17 default: 38
18 }
19 32: invokestatic #2 // Method bar:()V
20 35: invokestatic #2 // Method bar:()V
21 38: invokestatic #2 // Method bar:()V
22 41: return
23
24 public void foo();
25 Code:
26 0: ldc #3 // String
27 2: astore_1
28 3: aload_1
29 4: astore_2
30 5: iconst_m1
31 6: istore_3
32 7: aload_2
33 8: invokevirtual #4 // Method java/lang/String.hashCode:()I
34 11: lookupswitch { // 2
35 51: 36
36 52: 50
37 default: 61
38 }
39 36: aload_2
40 37: ldc #5 // String 3
41 39: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42 42: ifeq 61
43 45: iconst_0
44 46: istore_3
45 47: goto 61
46 50: aload_2
47 51: ldc #7 // String 4
48 53: invokevirtual #6 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
49 56: ifeq 61
50 59: iconst_1
51 60: istore_3
52 61: iload_3
53 62: lookupswitch { // 2
54 0: 88
55 1: 91
56 default: 94
57 }
58 88: invokestatic #2 // Method bar:()V
59 91: invokestatic #2 // Method bar:()V
60 94: invokestatic #2 // Method bar:()V
61 97: return
62
63 public static void bar();
64 Code:
65 0: return
66
67 static {};
68 Code:
69 0: iconst_0
70 1: putstatic #8 // Field c:I
71 4: return
72}而在 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's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。