太仓网站建设排名,互联网培训机构排名前十,微信抽奖小程序怎么做,交换友情链接的平台有哪些揭秘Java switch语句中的case穿透现象 1. switch 语句简介2. case穿透现象的原因关于 goto 3. switch和if的区别4. 总结 导语#xff1a;在 Java 开发中#xff0c;我们经常使用switch语句来进行条件判断和分支选择。然而#xff0c;有一个令人困惑的现象就是#xff0c;当… 揭秘Java switch语句中的case穿透现象 1. switch 语句简介2. case穿透现象的原因关于 goto 3. switch和if的区别4. 总结 导语在 Java 开发中我们经常使用switch语句来进行条件判断和分支选择。然而有一个令人困惑的现象就是当某个case语句没有加上break关键字时程序会继续执行下一个case语句这被称为case穿透现象。本文将揭秘case穿透现象的原因并解释为何会出现这种行为。
1. switch 语句简介
在开始揭秘case穿透现象之前我们先简单回顾一下switch语句的基本用法。switch语句用于根据变量的不同取值执行相应的代码块。其语法结构如下
switch (expression) {case value1:// 执行代码块1break;case value2:// 执行代码块2break;...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
22. 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_01: invokespecial #1 // Method java/lang/Object.init:()V4: returnLineNumberTable:line 8: 0public static void main(java.lang.String[]);Code:0: iconst_01: istore_12: iload_13: tableswitch { // 0 to 20: 281: 362: 44default: 52}28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;31: ldc #3 // String 033: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V36: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;39: ldc #5 // String 141: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V44: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;47: ldc #6 // String 249: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V52: returnLineNumberTable:line 10: 0line 11: 2line 13: 28line 15: 36line 17: 44line 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:()V4: returnLineNumberTable:line 8: 0public static void main(java.lang.String[]);Code:0: iconst_01: istore_12: iload_13: tableswitch { // 0 to 20: 281: 392: 50default: 58}28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;31: ldc #3 // String 033: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V36: goto 5839: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;42: ldc #5 // String 144: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V47: goto 5850: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;53: ldc #6 // String 255: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V58: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;61: ldc #7 // String Hello World63: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V66: returnLineNumberTable:line 10: 0line 11: 2line 13: 28line 14: 36line 16: 39line 17: 47line 19: 50line 22: 58line 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_01: invokespecial #1 // Method java/lang/Object.init:()V4: returnLineNumberTable:line 8: 0public static void main(java.lang.String[]);Code:0: iconst_01: istore_12: iload_13: ifne 166: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;9: iconst_010: invokevirtual #3 // Method java/io/PrintStream.println:(I)V13: goto 4316: iload_117: iconst_118: if_icmpne 3121: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;24: iconst_125: invokevirtual #3 // Method java/io/PrintStream.println:(I)V28: goto 4331: iload_132: iconst_233: if_icmpne 4336: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;39: iconst_240: invokevirtual #3 // Method java/io/PrintStream.println:(I)V43: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;46: ldc #4 // String Hello World48: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V51: return
}「ifne」和「if_icmpne」是Java字节码指令中的两个条件分支指令用于在程序执行过程中进行条件判断并跳转到相应的代码块。它们的区别在于操作数类型和比较方式。
ifne操作数类型为int功能是当栈顶元素不等于零时跳转到指定的代码块。if_icmpne操作数类型为int当两个int类型的数值不相等时跳转到指定的代码块。
从字节码也可以看出if和switch的区别
if条件和代码块的字节码是顺序的switch条件和代码块是分开的if自动生成goto指令switch只有加了break才生成goto指令。
4. 总结
case穿透现象指在switch语句中当某个case语句没有break程序会继续执行下一个case语句。case中的break作用是告诉前端编译器「给每个case对应代码块的最后加上goto」。这样执行完匹配上的代码之后就可以略过后面的case代码块了。switch都支持哪些类型呢 基本数据类型byte, short, char, int包装数据类型Byte, Short, Character, Integer枚举类型Enum字符串类型StringJdk 7 开始支持