2

经典中的隐藏关卡

学习任何一门编程语言,一定会遇到switch语句。switch的语法本身比较简单,大理石丛书之《Thinking in Java》也没为它消耗太多章节,仅从3个方面进行了解释和强调:

(1)switch是用来做选择的
(2)请注意选择因子的类型
(3)一定要写break语句
image.png

抛开"JDK8+支持了更丰富的选择因子数据类型"不谈,switch好像没啥可琢磨的。但,《Thinking in Java》却在示例代码中隐藏了一句匪夷所思的暗语:~~~~

To quiet the compiler
image.png

于是高老爷子就这样再次给我们埋下了语法彩(陷)蛋(阱)。。。

琢磨

您当然可以说,实战的时候,谁会把default放在case的前面?这显然违反了各大厂的编程规范啊!
的确如此,无论是阿里,还是谷歌,各大厂的编程规范都会这么约束:

(1)case语句一定要记得加break
(2)default放在所有case的最后

那么,编程规范为什么要这么约束呢?如果不遵循约束有怎样的后果呢?
根据编程规范,可以知道,失去了约束,我们将面对4种场景:

1.case语句加上了break,且default语句在所有case的最后
2.case语句加上了break,但default语句穿插于case之间
3.case语句未加上break,且default语句在所有case的最后
4.case语句未加上break,但default语句穿插于case之间

场景1:case语句加上了break,且default语句在所有case的最后

  • 如果case匹配上了,结果就是执行第一个匹配的case,然后就返回了

image.png

  • 如果case未匹配上,结果就是执行default,然后就返回了

image.png

场景2:case语句加上了break,但default语句穿插于case之间

  • 如果case匹配上了,结果就是执行第一个匹配的case,然后就返回了

image.png

  • 如果case未匹配上,结果就是执行default,然后就返回了

image.png

场景3:case语句未加上break,且default语句在所有case的最后

  • 如果case匹配上了,结果就是执行第一个匹配的case,但一直到第一次出现break才返回

image.png
但,马上就会有人反对:“会不会是因为default是一定会执行的呢?”,那我们再来证明一下:
image.png

  • 如果case未匹配上,结果就是执行default,然后就返回了

image.png

场景4:case语句未加上break,但default语句穿插于case之间

  • 如果case匹配上了,结果就是执行第一个匹配的case,但一直到第一次出现break才返回

image.png

  • 如果case未匹配上,结果就是执行default,但一直到第一次出现break才返回

image.png

琢磨的结论

场景1~场景3得到的结论都一样:如果能找到第一个匹配的case,则一直到第一次出现break才返回
场景4做出了进一步的补充:如果找不到第一个匹配的case,就执行default,一直到第一次出现break才返回

这也是编程规范为什么会进行约束——成本和效率——如果对breakdefault顺序不加约束,实战中,您将陷入预期结果的迷局!

继续琢磨

写到这里,似乎我们已经完全理解了switch。但,我们存在一个逻辑漏洞:我们把JDK当做黑盒,反推switch的规律——即,假定高老爷子是“上帝”,写了一段绝对正确的代码——好吧,让我们开启JVM理(秃)解(头)之旅。
image.png

来,先看一段,高老爷子将switch翻译成lookupswitch,指令匹配到2,PC寄存器指引着我们跳转到
38号指令(38、41、42号指令共同完成了System.out.println(2)),PC寄存器继续指引着我们跳转到45号指令,45号指令又跳转到56号指令,return了。
image.png

到这里,我们可以知道高老爷子定义了一张“switch决策表”,决策表中每个case分支都会在编译期记录对应的代码块的地址,case和default的顺序并不会决定到底哪个分支会被执行,真正影响执行结果的是决策表之后那些代码块最后一句goto,加了break就有goto,没加break就没有goto

我们再来看看default穿插在case中的代码示例:
image.png

至此,我们打开了高老爷子的黑盒,我们可以升华一下对switch的理解:

1.switch会被JVM转换为switch决策表+case代码块,决策表仅记录决策因子和case代码块地址
2.break会被JVM转换为goto return
3.如果能找到第一个匹配的case,JVM就会跳转到决策表之后的case对应的代码块,如果找不到第一个匹配的case,JVM就会跳转到default对应的代码块。
4.无论这个代码块是case的还是default的,没有break(即,没有goto语句),JVM就继续执行下一个代码块,直到遇到第一个break
image.png

琢磨的最后

朱光潜先生的随笔《咬文嚼字》,告诉我们无论阅读或写作,都要有咬文嚼字的精神,方能追求艺术的完美。编程亦如此——Java有很多类似的语法细节,经典著作中往往只是一句容易被忽略的话,却可能引发一个诡异的网上问题。
细品Java,可以先将JVM看作黑盒,找规律——这里找到的规律往往需要记忆。再将JVM看作白盒,找本质——这里找到的本质又可以减少规律的记忆负担。
好,恭喜您,您又变强了一点点。
image.png


猴王无敌
7 声望1 粉丝