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

本文从小姐姐的问题出发,研究break在switch中的作用。通过分析字节码,发现switch中case条件和代码块分开,少break会顺序执行后续代码。引入goto指令,加break可跳出switch。还对比了if和switch的字节码区别,指出break让编译器加goto跳过后续case。

前言

一个小姐姐拿着一个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查看:

tableswitchlookupswitch都用于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)才是学习新知识的动力。

95后小程序员,写的都是日常工作中的亲身实践,置身于初学者的角度从0写到1,详细且认真。文章会在公众号 [入门到放弃之路] 首发,期待你的关注。

感谢每一份关注

<think> 我们参考了用户提供的多个引用,这些引用详细说明了breakswitch-case语句中的作用。主要作用包括: 1. 防止"case穿透"现象:当某个case被执行后,如果没有break,程序会继续执行下一个case中的语句,直到遇到breakswitch结束。 2. 确保只执行匹配的case:使用break可以立即终止switch语句的执行,跳出switch结构。 此外,引用[3]还讨论了当case中有return语句时,break是否还有作用。结论是:return会立即结束当前函数,因此break会被执行,也没有必要。 根据以上信息,我们可以总结break的作用,并用代码示例说明。 </think> 在`switch-case`语句中,`break`的作用是**终止当前`case`的执行并跳出整个`switch`语句块**。具体作用和必要性如下: ### 一、核心作用 1. **防止"case穿透"(fall-through)** 当匹配到某个`case`后,若加`break`,程序会继续执行**后续所有`case`中的代码**(包括`default`),直到遇到`break`或`switch`结束。 **示例(无`break`导致穿透):** ```c int x = 2; switch (x) { case 1: printf("Case 1\n"); // 无break,继续向下执行 case 2: printf("Case 2\n"); // 匹配到此,输出后继续执行 case 3: printf("Case 3\n"); // 继续执行 default: printf("Default\n"); // 继续执行 } // 输出:Case 2\nCase 3\nDefault\n (非预期结果) ``` [^4][^5] 2. **确保仅执行匹配的`case`** 添加`break`后,执行完当前`case`逻辑立即退出`switch`,避免误执行其他分支。 **示例(正确使用`break`):** ```c int x = 2; switch (x) { case 1: printf("Case 1\n"); break; // break终止 case 2: printf("Case 2\n"); break; // 执行此分支后跳出 case 3: printf("Case 3\n"); break; default: printf("Default\n"); } // 输出:Case 2\n (预期结果) ``` [^1][^2] ### 二、特殊注意事项 - **当`case`中有`return`时,`break`无效** `return`会直接结束当前函数,此时`break`会被执行(可省略): ```c switch (ID) { case 1: return 1; // 函数直接返回,break会执行 case 2: return 2; // 无需break } ``` [^3] - **刻意利用穿透实现多分支合并** 某些场景(如多个`case`共享同一逻辑)可省略`break`: ```c char grade = 'B'; switch (grade) { case 'A': case 'B': // A和B均执行此逻辑 printf("Pass"); break; case 'F': printf("Fail"); break; } ``` ### 三、总结 | 场景 | 是否必要`break` | 原因 | |--------------------|----------------|--------------------------| | 标准单分支逻辑 | **必需** | 防止穿透到其他分支 | | 分支末尾有`return` | 无需 | `return`已退出函数 | | 多分支合并逻辑 | 可选 | 刻意穿透实现代码复用 | > **最佳实践**:除非刻意利用穿透特性,否则每个`case`末尾都应添加`break`以保证逻辑清晰[^1][^5]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值