switch 穿透(fall-through)是 C 系语言的老传统,Java 完整继承了这个特性——包括它的坑。这篇真实复现一段缺失 break 的代码,看结果错得有多不动声色。
上一篇:数组默认值真实验证
最经典的 switch 陷阱:case 1 后面漏写了 break,代码会继续往下执行 case 2 的语句。
SwitchFallthroughDemo.java switch (day) { case 1: result.add("周一"); // 忘记写 break case 2: result.add("周二"); break; ... }
buggyDescribe(1) 真实结果(缺break导致穿透): [周一, 周二] correctDescribe(1) 真实结果(有break): [周一]
真实结果证明了穿透 bug 的可怕之处:buggyDescribe(1) 本来只应该返回 "周一",但因为 case 1 缺少 break,程序真实继续执行了紧跟着的 case 2 分支,把 "周二" 也加了进去。整个过程没有任何编译错误(编译器不会强制要求每个 case 都有 break),运行时也没有抛出任何异常——这就是这类 bug 最危险的地方:代码"看起来正常运行",结果却真实是错的,只能靠仔细审查或测试用例才能发现。
不是所有的穿透都是bug,故意省略中间case的break,让多个case复用同一段代码,是一个正当的写法。
SwitchFallthroughDemo.java switch (day) { case 6: case 7: result = "周末"; break; default: result = "工作日"; }
weekendCheck(6) = 周末 weekendCheck(7) = 周末 weekendCheck(3) = 工作日
另一个容易被误解的点:switch 找不到匹配的 case 时,是不是会把前面所有 case 都执行一遍才走到 default?
switch(99) 无匹配case,真实结果: [D(真实:不匹配任何case时直接从default开始,不会经过前面的case)]
真实结果证明 switch 语句的执行模型不是"从上往下顺序扫描每个 case 语句块",而是先用条件值真实定位到匹配的标签(case 值或 default),然后从那个标签对应的字节码位置开始往下执行,直到遇到 break 或方法结束——这也是系列前面 switch 字节码那篇里验证过的 lookupswitch 跳转表机制的直接体现:每个 case 值对应一个明确的跳转地址,不存在的 case 值直接跳到 default 对应的地址,不会经过任何前面 case 的代码。
每一步的 result 内容都是真实运行这段代码得到的,不是按规则手工推的。