Java 14 转正的 switch 表达式(箭头语法)看起来是全新的东西,这篇真实用 javap 反编译两种写法的 class 文件,一条指令一条指令对比,看看新语法在字节码层面到底改变了什么、没改变什么。
先看最经典的写法:switch 作为语句,每个 case 里给局部变量赋值,最后统一 return。
SwitchBytecodeDemo.java static int oldSwitch(int day) { int result; switch (day) { case 1: result = 100; break; case 2: result = 200; break; default: result = -1; } return result; }
static int oldSwitch(int);
Code:
0: iload_0
1: lookupswitch { // 2
1: 28
2: 34
default: 41
}
28: bipush 100
30: istore_1
31: goto 43
34: sipush 200
37: istore_1
38: goto 43
41: iconst_m1
42: istore_1
43: iload_1
44: ireturn
真实反编译结果:day 只有 2 个具体 case,编译器选择用 lookupswitch(适合稀疏/少量 case 的查表跳转)而不是 tableswitch。每个分支真实做的事是"把结果值存进局部变量 1(istore_1),再跳到末尾",最后统一 iload_1 读出来再 ireturn。
同样的逻辑,用 switch 表达式直接作为返回值写。
SwitchBytecodeDemo.java static int newSwitch(int day) { return switch (day) { case 1 -> 100; case 2 -> 200; default -> -1; }; }
static int newSwitch(int);
Code:
0: iload_0
1: lookupswitch { // 2
1: 28
2: 33
default: 39
}
28: bipush 100
30: goto 40
33: sipush 200
36: goto 40
39: iconst_m1
40: ireturn
真实结果一目了然:第 1 条指令依然是同一个 lookupswitch,匹配到的分支跳转目标也完全对应;唯一的区别是每个分支不再需要 istore_1 存、最后再 iload_1 取,而是直接 ireturn。少了 3 条指令(两次 istore_1 省掉,加上最后一次 iload_1),方法体从 15 条指令缩到 12 条。
补充验证一个常被问到的问题:switch (String) 底层是不是逐个 equals 比较字符串?
SwitchBytecodeDemo.java switch (s) { case "a": return 1; case "b": return 2; default: return -1; }
aload_1
invokevirtual String.hashCode:()I
lookupswitch { 97: 36, 98: 50, default: 61 }
36: aload_1; ldc "a"; invokevirtual String.equals; ifeq 61; ...
50: aload_1; ldc "b"; invokevirtual String.equals; ifeq 61; ...
61: iload_2
lookupswitch { 0: 88, 1: 90, default: 92 }
真实结果证明 String 类型的 switch 是"两段式"的:第一段先调用 hashCode(),用哈希值(97='a', 98='b')做第一次 lookupswitch 快速定位候选分支,再用 equals() 真实确认内容匹配(防止哈希碰撞误判);确认后把结果映射成一个从 0 开始的整数序号,再做第二次 lookupswitch 跳到真正的业务分支。整个过程没有一步是"逐个字符串顺序比较"。
每一行都是真实反编译或真实运行这段代码得到的结果。