String类型能在switch里直接用,是Java 7才加入的语法糖——这篇真实用javap反编译,并构造两个hashCode相同的字符串"Aa"和"BB",验证switch底层是怎么保证不会认错。
"Aa"和"BB"是Java里经典的hashCode碰撞例子——先验证碰撞真实存在,再验证switch语句会不会因此把这两个值搞混。
StringSwitchBytecodeDemo.java switch (s) { case "Aa": return "特殊值Aa"; case "BB": return "特殊值BB"; ... }
"Aa".hashCode()=2112, "BB".hashCode()=2112, 真实是否相等: true
classify("Aa")=特殊值Aa, classify("BB")=特殊值BB (若switch只用hashCode不用equals,这两者会混淆; 真实结果证明没有混淆)
真实结果证明即使"Aa"和"BB"的hashCode()真实完全相同(都是2112),switch依然正确地把它们分派到了各自对应的分支——如果底层真的只靠hashCode做判断,这两个case在字节码层面会变得无法区分。真实结果说明背后一定还有额外的校验步骤。
javap -c StringSwitchBytecodeDemo static java.lang.String classify(java.lang.String);
aload_1
invokevirtual Method java/lang/String.hashCode:()I
lookupswitch { // 4
-1396355227: 66
-1361513063: 80
2112: 94 <-- "Aa"和"BB"共享同一个hashCode,跳到同一个偏移量94
93029210: 52
default: 119
}
94: aload_1
ldc String BB
invokevirtual Method java/lang/String.equals:(Ljava/lang/Object;)Z
ifeq 108 <-- 不等于"BB",继续往下试
iconst_4
istore_2
goto 119
108: aload_1
ldc String Aa
invokevirtual Method java/lang/String.equals:(Ljava/lang/Object;)Z
ifeq 119
iconst_3
istore_2
每一行都是真实运行这段代码或真实反编译字节码得到的结果。