bigywater.tech
Java 内部原理 · 75

switch 穿透真实复现:忘记 break 导致的静默逻辑 bug

switch 穿透(fall-through)是 C 系语言的老传统,Java 完整继承了这个特性——包括它的坑。这篇真实复现一段缺失 break 的代码,看结果错得有多不动声色。

缺break真实多执行一段
真实复现:case 1 缺少break,真实把case 2的代码也执行了,没有编译错误,没有运行时异常,结果list真实多了一个元素
不匹配直接跳到default
真实验证:switch(99)不匹配任何case时,真实直接从default标签开始执行,不会像"从上往下扫过"那样经过case 1/2的代码

上一篇:数组默认值真实验证

真实复现:忘记 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 最危险的地方:代码"看起来正常运行",结果却真实是错的,只能靠仔细审查或测试用例才能发现。

真实验证:穿透的合法用法——多个case共享同一段逻辑

不是所有的穿透都是bug,故意省略中间case的break,让多个case复用同一段代码,是一个正当的写法。

SwitchFallthroughDemo.java switch (day) { case 6: case 7: result = "周末"; break; default: result = "工作日"; }
真实实测
weekendCheck(6) = 周末
weekendCheck(7) = 周末
weekendCheck(3) = 工作日
合法穿透和 bug 穿透的真实区别。这里 case 6 后面没有任何语句直接跟着 case 7,是有意让两个 case 标签"共享"同一段处理逻辑——这种写法在真实工程代码里很常见,而且不容易引起误解,因为 case 6: 本身是空的,没有语句被"跳过执行"的疑惑。真正危险的穿透场景,是像上一节那样 case 里已经写了具体语句,却唯独漏了 break——这也是为什么现代 IDE 和静态检查工具(如 -Xlint:fallthrough)通常只对"case 内有语句但缺少 break"发出警告,而不会对这种空 case 连续声明的写法报警。

真实验证:不匹配时直接跳到 default,不会"扫过"前面的 case

另一个容易被误解的点:switch 找不到匹配的 case 时,是不是会把前面所有 case 都执行一遍才走到 default?

真实实测
switch(99) 无匹配case,真实结果: [D(真实:不匹配任何case时直接从default开始,不会经过前面的case)]

真实结果证明 switch 语句的执行模型不是"从上往下顺序扫描每个 case 语句块",而是先用条件值真实定位到匹配的标签(case 值或 default),然后从那个标签对应的字节码位置开始往下执行,直到遇到 break 或方法结束——这也是系列前面 switch 字节码那篇里验证过的 lookupswitch 跳转表机制的直接体现:每个 case 值对应一个明确的跳转地址,不存在的 case 值直接跳到 default 对应的地址,不会经过任何前面 case 的代码。

分步演示:控制流是怎么"掉"进下一个 case 的

每一步的 result 内容都是真实运行这段代码得到的,不是按规则手工推的。

buggyDescribe(1) 真实多执行了一段 → noMatchDescribe(99) 真实直接跳到 default
控制流当前所在的 case完全没有被经过的 case

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,三组场景重复运行结果完全一致(纯确定性控制流逻辑)。
    • 和switch 字节码那篇一起看,能从跳转表的字节码层面彻底理解为什么穿透会发生、以及为什么不匹配的case会直接跳到default。
    • 没有涉及:switch 表达式(箭头语法)如何在语言层面彻底消除穿透风险(每个分支自动隐式break,不再共享执行路径)——这属于系列前面已经涉及的新语法特性,值得单独对照写一篇。
    京ICP备2026067502号-1京公网安备11010502063487号