为什么switch里的case没有break不行

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

[[412376]]

 前言

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

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

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

 从字节码出发

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

  1. public static void main(String[] args) {  
  2.    int i = 0 
  3.    switch (i) {  
  4.        case 0:  
  5.            System.out.println(0);  
  6.        case 1:  
  7.            System.out.println(1);  
  8.        case 2:  
  9.            System.out.println(2);  
  10.  } 

运行代码,结果如下:

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

  1. public static void main(String[] args) {  
  2.     int i = 0 
  3.     switch (i) {  
  4.         case 0:  
  5.             System.out.println(0);  
  6.             break;  
  7.         case 10:  
  8.             System.out.println(1);  
  9.             break;  
  10.         case 2:  
  11.             System.out.println(2);  
  12.             break;  
  13.     }  
  14.     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逻辑。 

  1. public static void main(String[] args) {  
  2.     int i = 0 
  3.     if (i == 0) {  
  4.         System.out.println(0);  
  5.     } else if (i == 1) {  
  6.         System.out.println(1);  
  7.     } else if (i == 2) {  
  8.         System.out.println(2);  
  9.     }  

编译成字节码:

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

结语

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

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

 

责任编辑:庞桂玉 来源: Hollis
相关推荐

2020-07-16 15:20:13

switch...caif...else语言

2023-06-26 07:31:29

中文编程编码

2009-06-08 21:45:46

Javaswitch-case

2013-01-22 09:35:27

Hadoop存储

2021-04-05 14:47:05

装饰器Python代码

2009-08-19 10:41:14

C# switch和c

2011-05-25 14:59:35

if elseswitch case

2021-05-08 08:55:54

CPUIBMIntel

2020-10-09 06:48:19

Pythonswitch语句

2022-06-12 21:36:57

Hooksreact

2021-04-20 19:23:07

语法switch-casePython

2017-01-13 23:06:45

swiftios

2011-07-21 11:11:10

Scrum

2015-08-06 10:19:19

编程脑子

2010-02-02 17:20:44

C++ switch-

2020-07-22 07:55:12

Python开发函数

2021-01-20 12:44:22

JAVA编程语言软件

2021-01-20 12:43:07

编程语言Java

2019-08-15 16:48:30

2022-12-01 08:15:41

CGOswitcherror
点赞
收藏

51CTO技术栈公众号