写点什么

为什么 switch 的 case 没有 break 不行

发布于: 1 小时前
为什么switch的case没有break不行

前言

一个小姐姐拿着一个 switch 的选择题来问我。



之所以这么笃定地回答这个问题,并不是我知道其中原理,而是之前在一个群里,有人问了同类型的问题,我瞥了一眼记住了答案,所以才依葫芦画瓢。


小姐姐接着问我为什么,我说少个 break,但凡再问一句:为什么少个 break 结果就不一样,我就回答不出来了。所以,为了将尴尬扼杀于摇篮,还是研究一下 break 在 switch 的作用。

从字节码出发

按照惯例,先写 demo 表述问题。


 public static void main(String[] args) {    int i = 0;    switch (i) {        case 0:            System.out.println(0);        case 1:            System.out.println(1);        case 2:            System.out.println(2);  }
复制代码


运行代码,结果如下:



*明明只匹配了 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。


  public static void main(String[] args) {      int i = 0;      switch (i) {          case 0:              System.out.println(0);              break;          case 10:              System.out.println(1);              break;          case 2:              System.out.println(2);              break;      }      System.out.println("Hello World");  }
复制代码


重新编译,再来看看字节码。



如图,与第一次的字节码相比,在标号 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 逻辑。


  public static void main(String[] args) {      int i = 0;      if (i == 0) {          System.out.println(0);      } else if (i == 1) {          System.out.println(1);      } else if (i == 2) {          System.out.println(2);      }  }
复制代码


编译成字节码:



if_icmpne 用于比较两个 int 数。从字节码也可以看出 if 和 switch 的区别:if 条件和代码块的字节码是顺序的,switch 条件和代码块是分开的;if 自动生成 goto 指令,switch 只有加了 break 才生成 goto 指令

结语

case 中的 break 告诉前端编译器:给每个 case 对应代码块的最后加上 goto。这样,执行完匹配上的代码之后,就可以略过后面的 case 代码块了。


果然,求(xiao)知(jie)欲(jie)才是学习新知识的动力。


发布于: 1 小时前阅读数: 2
用户头像

公众号:入门到放弃之路 2021.05.23 加入

公众号:入门到放弃之路。自学Java、python、大数据。

评论

发布
暂无评论
为什么switch的case没有break不行