为什么 switch 的 case 没有 break 不行
前言
一个小姐姐拿着一个 switch 的选择题来问我。
之所以这么笃定地回答这个问题,并不是我知道其中原理,而是之前在一个群里,有人问了同类型的问题,我瞥了一眼记住了答案,所以才依葫芦画瓢。
小姐姐接着问我为什么,我说少个 break,但凡再问一句:为什么少个 break 结果就不一样,我就回答不出来了。所以,为了将尴尬扼杀于摇篮,还是研究一下 break 在 switch 的作用。
从字节码出发
按照惯例,先写 demo 表述问题。
运行代码,结果如下:
*明明只匹配了 case 0,为什么 1 和 2 也执行了? 很费解!按照惯用套路,看看字节码能不能给个答案。
javac 编译和 javap 查看:
tableswitch 和 lookupswitch 都用于 switch 条件跳转,前者用于 case 值连续,例如上面代码中的 0、1、2;后者用于 case 值不连续。
从字节码可以看出:switch 中的 case 条件和对应代码块是分开的。如上图,case 为 0 时,跳转到标号 28 代码处;为 1 时跳转到标号 35 代码处;为 2 时跳转到标号 43 代码处;default 则跳转到标号 49 代码处。
这不,答案就出来了,当 case 0 匹配了之后,直接跳转到标号 28 代码处开始执行,输出 0,然后策马奔腾,一路小下坡,顺序执行完后面所有代码,直到标号 49 return,方法完执行完成,程序结束。
如果按照正常的思维,是不是 case 0 匹配之后,跳到 28,执行完 28、31、32 输出 0 之后,就应该直接跳走,直接执行 49。那么,这个"跳走”用字节码应该怎么表示?
用 return?那不行,因为 return 会结束方法,这样 switch 后代码也无法执行。那怎么办嘞....
关于 goto
goto:无条件跳转,goto 1 表示跳转到标号 1 的代码处。
再写代码样例,这次在代码中给每个 case 都加上 break。
重新编译,再来看看字节码。
如图,与第一次的字节码相比,在标号 35、45 都有了 goto 指令。如果 case 0 匹配成功,则跳到标号 28 执行,执行完代码块对应的 31、32 指令之后,执行 35 的 goto 指令跳转到标号 55,这样就跳出了 switch 作用范围,case 1 和 2 也不会被执行。
等等,怎么少了一个 goto,在标号 55 的上方应该还有一个 goto 才对!其实这就涉及到了编译器优化技术,最后一个 goto 也是跳转到标号 55 的指令,但没有 goto 下一步也一样顺序执行此行指令,所以这个 goto 被编译器视为无用代码进行了消除。
switch 和 if 区别
先用 if 实现上面 switch 逻辑。
编译成字节码:
if_icmpne 用于比较两个 int 数。从字节码也可以看出 if 和 switch 的区别:if 条件和代码块的字节码是顺序的,switch 条件和代码块是分开的;if 自动生成 goto 指令,switch 只有加了 break 才生成 goto 指令。
结语
case 中的 break 告诉前端编译器:给每个 case 对应代码块的最后加上 goto。这样,执行完匹配上的代码之后,就可以略过后面的 case 代码块了。
果然,求(xiao)知(jie)欲(jie)才是学习新知识的动力。
版权声明: 本文为 InfoQ 作者【叫我阿柒啊】的原创文章。
原文链接:【http://xie.infoq.cn/article/fd13f12ffc370b96c2fcad6f9】。文章转载请联系作者。
评论