揭秘Java switch语句中的case穿透现象

这篇具有很好参考价值的文章主要介绍了揭秘Java switch语句中的case穿透现象。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

导语:在 Java 开发中,我们经常使用switch语句来进行条件判断和分支选择。然而,有一个令人困惑的现象就是,当某个case语句没有加上break关键字时,程序会继续执行下一个case语句,这被称为case穿透现象。本文将揭秘case穿透现象的原因,并解释为何会出现这种行为。

1. switch 语句简介

在开始揭秘case穿透现象之前,我们先简单回顾一下switch语句的基本用法。switch语句用于根据变量的不同取值执行相应的代码块。其语法结构如下:

switch (expression) {
    case value1:
        // 执行代码块1
        break;
    case value2:
        // 执行代码块2
        break;
    ...
    default:
        // 默认代码块
}

switch case支持的6种数据类型:switch 表达式后面的数据类型只支持byte、short、int整形类型、字符类型char、枚举类型和java.lang.String类型。

根据expression的值,程序会跳转到对应的case语句进行匹配并执行相应的代码块,直到遇到break关键字或者到达switch语句的结尾。

如果某个case语句没有break,程序会继续执行下一个case语句,这就是case穿透现象

我们看下面这个例子。

public class Test {
    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");
        }
    }
}

打印结果:

0
1
2

2. case穿透现象的原因

按照惯用套路,看看字节码能不能给个答案。

javac编译javap查看

> javap -c -l Test.class
Compiled from "Test.java"
public class com.atu.algorithm.aTest.Test {
  public com.atu.algorithm.aTest.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 8: 0

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: tableswitch   { // 0 to 2
                     0: 28
                     1: 36
                     2: 44
               default: 52
          }
      28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: ldc           #3                  // String 0
      33: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      36: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      39: ldc           #5                  // String 1
      41: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      44: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      47: ldc           #6                  // String 2
      49: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      52: return
    LineNumberTable:
      line 10: 0
      line 11: 2
      line 13: 28
      line 15: 36
      line 17: 44
      line 19: 52
}

根据提供的字节码,我们来解释一下case穿透的情况。

在main方法中,通过tableswitch指令实现了一个switch语句。switch语句会根据值进行跳转,并执行对应的代码块。

在这个例子中,根据tableswitch指令的参数 {0 to 2}case的范围是从0到2。

  • switch的表达式的值为0时,程序会跳转到标签为28的位置,然后继续执行28标签处的代码块。
  • 为1时跳转到标号36代码处;
  • 为2时跳转到标号44代码处;
  • default则跳转到标号52代码处。

这不,答案就出来了,当case 0匹配了之后,直接跳转到标号28代码处开始执行,输出0,然后策马奔腾,一路下坡,顺序执行完后面所有代码,直到标号52 return,方法完执行完成,程序结束。

如果按照正常的思维,是不是case 0匹配之后,跳到28,执行完28、31、32输出0之后,就应该直接跳走,直接执行49。

那么,这个【跳走】用字节码应该怎么表示?

关于 goto

再写代码样例,这次我们在代码中给每个case都加上break

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

打印结果:

0
Hello World

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

Compiled from "Test.java"
public class com.atu.algorithm.aTest.Test {
  public com.atu.algorithm.aTest.Test();   
    Code:                                  
       0: aload_0                                                                  
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 8: 0

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: tableswitch   { // 0 to 2
                     0: 28
                     1: 39
                     2: 50
               default: 58
          }
      28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: ldc           #3                  // String 0
      33: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      36: goto          58
      39: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: ldc           #5                  // String 1
      44: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      47: goto          58
      50: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      53: ldc           #6                  // String 2
      55: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      58: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      61: ldc           #7                  // String Hello World
      63: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: return
    LineNumberTable:
      line 10: 0
      line 11: 2
      line 13: 28
      line 14: 36
      line 16: 39
      line 17: 47
      line 19: 50
      line 22: 58
      line 23: 66
}

如图,与第一次的字节码相比,在标号36、47都有了goto指令。如果case 0匹配成功,则跳到标号28执行,执行完代码块对应的31、33指令之后,执行36的goto指令跳转到标号58,这样就跳出了switch作用范围,case 1和2也不会被执行。

在Java字节码中,goto指令用于无条件跳转到指定的目标代码块。它可以实现程序的跳转和循环控制。

等等,怎么少了一个goto,在标号58的上方应该还有一个goto才对!

其实这就涉及到了编译器优化技术,最后一个goto也是跳转到标号58的指令,但没有goto下一步也一样顺序执行此行指令,所以这个goto被编译器视为无用代码进行了消除。

3. switch和if的区别

先用if实现上面switch逻辑。

public class Test {
    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);
        }
        System.out.println("Hello World");
    }
}

编译成字节码

Compiled from "Test.java"
public class com.atu.algorithm.aTest.Test {
  public com.atu.algorithm.aTest.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 8: 0

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifne          16
       6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: iconst_0
      10: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      13: goto          43
      16: iload_1
      17: iconst_1
      18: if_icmpne     31
      21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      24: iconst_1
      25: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      28: goto          43
      31: iload_1
      32: iconst_2
      33: if_icmpne     43
      36: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      39: iconst_2
      40: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      43: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      46: ldc           #4                  // String Hello World
      48: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      51: return
}

「ifne」和「if_icmpne」是Java字节码指令中的两个条件分支指令,用于在程序执行过程中进行条件判断并跳转到相应的代码块。它们的区别在于操作数类型和比较方式。

  • ifne:操作数类型为int,功能是当栈顶元素不等于零时,跳转到指定的代码块。
  • if_icmpne:操作数类型为int,当两个int类型的数值不相等时,跳转到指定的代码块。

从字节码也可以看出ifswitch的区别:文章来源地址https://www.toymoban.com/news/detail-739771.html

  • if条件和代码块的字节码是顺序的,switch条件和代码块是分开的;
  • if自动生成goto指令,switch只有加了break才生成goto指令。

4. 总结

  1. case穿透现象:指在switch语句中,当某个case语句没有break,程序会继续执行下一个case语句。
  2. case中的break作用是告诉前端编译器:「给每个case对应代码块的最后加上goto」。这样,执行完匹配上的代码之后,就可以略过后面的case代码块了。
  3. switch都支持哪些类型呢?
    • 基本数据类型:byte, short, char, int
    • 包装数据类型:Byte, Short, Character, Integer
    • 枚举类型:Enum
    • 字符串类型:String(Jdk 7+ 开始支持)

到了这里,关于揭秘Java switch语句中的case穿透现象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • switch语句每个case后一定要有break吗?

    java和c/c++差不多,就用java代码解释吧 先说结论:不一定要break!!! 如果case后面没有break,那么则会无视下个case的条件执行下一个case,直到再次出现一个break跳出循环 上对比代码: 代码的意思就是键盘输入一个a,如果a=1,就执行case中的内容。 我们输入1,结果是2(如图:

    2024年02月11日
    浏览(25)
  • C# switch case语句入门and业务必知点

    具体的语法形式如下。 switch(表达式) {     case 值 1:         语句块 1;         break;     case 值 2:         语句块 2;         break;         ...     default:         语句块 n;         break; } 在这里,switch 语句中表达式的结果必须是整型、字符串类型、字符型、布尔型等数据

    2023年04月21日
    浏览(24)
  • 看完这篇文章,保你学会C语言switch case 语句

    我的个人主页: ☆光之梦☆的博客_CSDN博客-C语言基础语法(超详细)领域博主 欢迎各位 👍点赞 ⭐收藏 📝评论 特别标注 :本博主将会长期更新c语言的语法知识,初学c语言的朋友们,可以收藏订阅一下我的专栏: C语言基础语法(超详细)_☆光之梦☆的博客-CSDN博客 (这

    2023年04月26日
    浏览(32)
  • 【零基础学JS - 14 】javaScript中的switch语句

    👨‍💻 作者简介:程序员半夏 , 一名全栈程序员,擅长使用各种编程语言和框架,如JavaScript、React、Node.js、Java、Python、Django、MySQL等.专注于大前端与后端的硬核干货分享,同时是一个随缘更新的UP主. 你可以在各个平台找到我! 🏆 本文收录于专栏: 零基础学JavaScript,包含Jav

    2024年02月07日
    浏览(26)
  • SQL中的CASE WHEN语句:从基础到高级应用指南

    我们使用一个名为\\\"Products\\\"的表,包含以下列:ProductID、ProductName、CategoryID、UnitPrice、StockQuantity。 示例展示 productID productName categoryID unitPrice stockQuantity 1 Laptop 1 800 50 2 Smartphone 1 500 100 3 T-shirt 2 20 200 4 Jeans 2 40 150 5 Headphones 1 100 75 1. CASE WHEN-基本使用 查询结果: ProductName UnitPric

    2024年02月09日
    浏览(60)
  • Java基础入门篇——Switch条件语句(十一)

    目录 一、switch条件语句 二、Scanner类使用 三、数据的输出 四、continue语句使用 Switch语句是一种条件语句,用于基于不同的条件值执行不同的代码块。它可以简化多个 if-else if-else 嵌套语句的结构。  Switch条件语句的基本结构: 在Switch语句中,一个表达式的值将与多个case进行

    2024年02月13日
    浏览(32)
  • 超级进化吧switch case in java

    因为长情是古董,所以假货特别多 觉得太长可以直接看总结就可以了 Switch case语句在Java中是一种流程控制结构,用于将一个值与一系列可能的情况进行比较,并根据匹配的情况执行相应的代码块。在不同的Java版本中,switch case标签的用法略有不同。 Java 6及以下 | 整数当道

    2023年04月08日
    浏览(27)
  • java jdk8 switch case中无法使用枚举问题解决

    之前常规写法: 代码会报错: 此时在枚举中添加方法: 代码中改为: idea自编译不报错, 经测试也可以用 枚举完整代码: 参考: 「Java基础入门」Java中switch怎么使用枚举 - 掘金

    2024年04月25日
    浏览(30)
  • 【算法笔记】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

    求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等及条件判断语句(A?B:C)、位运算、递归、公式、sizeof。 习题链接:题目链接 该题把我们的所有能用的方法都给限制死了,我们只能用一些的特殊的方法来做。不知道大家在思考的过程中考没考虑过

    2024年02月07日
    浏览(46)
  • 【Dart】=> [04] Dart初体验-基础语法(流程控制-if-switch-case-for循环

    学习内容: if 语句 switch – case 语句 for 循环语句 if 语句让代码有选择的执行,可以指定满足条件时才能执行的代码。 语句形式: if … else if … else if … else 案例: 根据学生分数,判断学生成绩是否及格 根据学生分数,划分学生成绩所在等级 优秀:分数大于等于90分 良好

    2024年01月19日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包