jdk1.7 String switch的实现
对于int的switch,jvm是用tableswitch和lookupswitch来实现的,jdk1.7 switch增加了对string的支持,那么底层是如何实现的呢?是否增加了新的指令或是否给某些指令增加了新的含义?
看这样一个程序:
public class Test {
public static void main(String[] args) {
String name = "b";
int value = 0;
switch(name) {
case "a":
value = 1;
break;
case "b":
value = 2;
break;
case "c":
value = 3;
break;
case "d":
value = 4;
break;
case "e":
value = 5;
break;
default:
value = 6;
}
System.out.println(value);
}
}
javap -c Test得出的结果为:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String b
2: astore_1 //将"b"赋值给name
3: iconst_0 //将0入栈
4: istore_2 //将0赋值给value
5: aload_1 //将name(即"b")入栈
6: astore_3 //将name(即"b")赋值给一个编译器生成的变量,记为tmpName(tmpName此时也是"b")
7: iconst_m1 //将-1入栈
8: istore 4 //将-1赋值给一个编译器生成的变量,记为m1
10: aload_3 //将tmpName(即"b")入栈
11: invokevirtual #3 // Method java/lang/String.hashCode:()I 调用tmpName的hashCode方法("b".hashCode(),得结果98)
14: tableswitch { // 97 to 101 //根据hashCode的值到不同的分支
97: 48
98: 63 //这里走到这个分支,跳转到63
99: 78
100: 93
101: 108
default: 120
}
48: aload_3
49: ldc #4 // String a
51: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
54: ifeq 120
57: iconst_0
58: istore 4
60: goto 120
63: aload_3 //从14跳转到了这里,将tmpName(即"b")入栈
64: ldc #2 // String b 将"b"入栈
66: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
//比较tmpName和"b",如果相等,将1入栈,不等将0入栈.这里是相等的,入栈的为1
69: ifeq 120 //从栈顶取出比较结果,如果等于0,就跳到120,如果不等于0就继续下面的指令,这里显然不等于0
72: iconst_1 //将1入栈
73: istore 4 //将1存储到m1中
75: goto 120 //跳到120
78: aload_3
79: ldc #6 // String c
81: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
84: ifeq 120
87: iconst_2
88: istore 4
90: goto 120
93: aload_3
94: ldc #7 // String d
96: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
99: ifeq 120
102: iconst_3
103: istore 4
105: goto 120
108: aload_3
109: ldc #8 // String e
111: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
114: ifeq 120
117: iconst_4
118: istore 4
120: iload 4 //将m1(即73行存进去的1)的值入栈
122: tableswitch { // 0 to 4
0: 156
1: 161 //这里走1这个分支,跳到161
2: 166
3: 171
4: 176
default: 181
}
156: iconst_1
157: istore_2
158: goto 184
161: iconst_2 //将2入栈
162: istore_2 //将2存储到value
163: goto 184 //跳转到184进行打印输出
166: iconst_3
167: istore_2
168: goto 184
171: iconst_4
172: istore_2
173: goto 184
176: iconst_5
177: istore_2
178: goto 184
181: bipush 6
183: istore_2
184: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
187: iload_2
188: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
191: return
}
在这一堆指令中我们发现,jdk1.7并没有新指令来处理string switch,还是继续用lookupswitch和tableswitch两个指令来处理的,也没有扩展这两个指令,它们还是只能处理int。
在11行,我们看到调用了需switch的string的hashCode方法,并对该hashCode进行switch,并跳转到相应的处理指 令。跳到新的指令处我们发现这里(63行)将待switch的变量(name)与case中的值("b")equals了一下。这样做是为了避免不同的 string有相同的hashCode,确定equals返回true后,编译器生成了一个处理value的switch,源码里有多少个case编译器 生成的tableswitch就有多少个分支,最终会找到相应的处理分支完成string switch的处理。
接下来,看一个不同string hashCode相等的版本:
buzzards与righto的hashCode相等
hierarch与crinolines的hashCode相等
这里选择buzzards和righto
public class Test {
public static void main(String[] args) {
String name = "buzzards";
int value = 0;
switch(name) {
case "buzzards":
value = 1;
break;
case "righto":
value = 2;
break;
default:
value = 6;
}
System.out.println(value);
}
}
字节码:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String buzzards欠款
2: astore_1
3: iconst_0
4: istore_2
5: aload_1
6: astore_3
7: iconst_m1
8: istore 4
10: aload_3
11: invokevirtual #3 // Method java/lang/String.hashCode:()I
14: lookupswitch { // 1
-931102253: 32
default: 59
}
32: aload_3
33: ldc #4 // String righto
35: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)
38: ifeq 47
41: iconst_1
42: istore 4
44: goto 59
47: aload_3
48: ldc #2 // String buzzards
50: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)
53: ifeq 59
56: iconst_0
57: istore 4
59: iload 4
61: lookupswitch { // 2
0: 88
1: 93
default: 98
}
88: iconst_1
89: istore_2女装品牌大全
90: goto 101
93: iconst_2
94: istore_2
95: goto 101
98: bipush 6
100: istore_2
101: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
104: iload_2
105: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
108: return