前言一位小姐姐问了我一个switch的选择题。我之所以这么自信地回答这个问题,并不是我知道原理,而是之前有人在群里问过同类型的问题,我看了一眼就记住了答案,于是就照做了。那位女士又问我为什么,我说少了一个break,但是如果我再问一个问题:为什么break少了结果不一样,我答不上来。所以,为了把尴尬扼杀在摇篮里,让我们来研究一下开关的break功能。从字节码入手,按照惯例,先写demo表达问题。publicstaticvoidmain(String[]args){inti=0;switch(i){case0:System.out.println(0);case1:System.out.println(1);case2:System.out.println(2);}运行代码,结果如下:*明明只匹配到case0,为什么1和2也执行了?很费解!按照通常的套路,看字节码能不能给出答案。javac编译和javap查看:“tableswitch”和“lookupswitch”都用于switch条件跳转。前者用于连续的case值,如上面代码中的0、1、2;后者用于不连续的案例值。从字节码可以看出switch中的case条件与对应的代码块是分开的。如上图,当case为0时,跳转到codenumber28;为1时,跳转到代码35;为2时,跳转到43号码;默认情况下,它会跳转到代码编号49。不,答案就出来了。当匹配到case0时,直接跳转到标记为28的代码处开始执行,输出0,然后纵马驰骋,一路下坡,依次执行后面的所有代码,直到标记为49的代码返回,执行方法完成,程序结束。按照正常的思路,匹配到case0后,跳转到28,执行完28、31、32输出0后,应该直接跳过,直接执行49。那么,这个“跳远”应该如何用字节码来表达呢?使用返回?那不行,因为return会结束这个方法,所以switch之后的代码就不能执行了。那怎么办呢...关于gotogoto:无条件跳转,goto1表示跳转到label为1的代码。再写一遍代码示例,这次在代码中的每个case都加了一个break。publicstaticvoidmain(String[]args){inti=0;switch(i){case0:System.out.println(0);break;case10:System.out.println(1);break;case2:System.out。println(2);break;}System.out.println("HelloWorld");}重新编译,再看字节码。如图所示,相对于第一个字节码,标号35和45处有goto指令,如果case0匹配成功,则跳转到标号28处执行。执行完代码块对应的31和32指令后,再执行35的goto指令,跳转到标号55,从而跳出switch的范围,case1和2不会执行。等等,为什么少了一个goto,标签55上面应该有一个goto!其实这就涉及到编译器优化技术。最后一个goto也是跳转到标为55的指令,但是没有goto,下一步会依次执行这行指令,所以这个goto被编译器认为无用而淘汰。switch和if的区别首先使用if来实现上面的switch逻辑。publicstaticvoidmain(String[]args){inti=0;if(i==0){System.out.println(0);}elseif(i==1){System.out.println(1);}elseif(i==2){System.out.println(2);}}被编译成字节码:“if_icmpne”用于比较两个int数。if和switch的区别也可以从字节码看出:if条件和代码块的字节码是顺序的,switch条件和代码块是分开的;如果自动生成一个goto指令,switch只生成一个带有break指令的goto。epiloguecase中的break告诉前端编译器:“将goto添加到每个case对应的代码块的末尾”。这样匹配的代码执行完后,后面的case代码块就可以跳过了。果然求(xiao)知(jie)欲(jie)是学习新知识的动力。
