为什么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)才是学习新知识的动力。
95后小程序员,写的都是日常工作中的亲身实践,置身于初学者的角度从0写到1,详细且认真。文章会在公众号 [入门到放弃之路] 首发,期待你的关注。
为什么switch里的case没有break不行的更多相关文章
- switch中的case不加break执行情况
输出结果:230 分析,switch先匹配一个case满足$a,然后执行case里面的语句,直到遇到break,否则一直往下执行 <?php $a = ; switch($a){ : echo ...
- switch(值){ 开始case 值: 闭合break; }
switch ($goods['leixing']) { case 1: $data['type'] = 1; $data['orderid'] = 'PT' . rand(000000, 99999 ...
- switch的穿透,是参数里包含case内容就执行。
package rom; import java.lang.*; /* * switch的穿透,是参数里包含case内容就执行. */ public class Xamle_5 { public st ...
- Switch语句的case穿透
Switch语句的case穿透 一 switch语句几点说明: 1. case后面只能是常量,不能是变量,而且,多个case后面的值不能出现相同的. 2.case后面表达式可以接受: 基本数据类型,b ...
- 在switch中的case语句中声明变量编译出错的解决方案
在switch中的case语句中声明变量编译的问题 先来看段代码,别管什么意思: : , j = ; ; i < ; i++) recive_phone[i] = msgbuf.text[i]; ...
- 在微信公众号"码海"里学了一招:在update语句里使用case when 以避免多次更新导致的数据异常.
需求:将emp表中工资大于一万的降到9成,工资少于一万的乘以1.2. 难点:如果分成两句update执行,在10000附近的值可能会执行两次. 钥匙:在update语句里采用case when,使更新 ...
- java中的switch case default break
package com.didispace; /** * Created by gmq on 2017/08/07. * * @version 1.0 * @since 2017/08/07 10:4 ...
- switch case 忘记 break
昨天纠结了很久的问题,一直找不到原因,早上发现是一个低级错误: private void selectButton(int id) { switch (id) { case 0: recommend_ ...
- PHP switch分支语句中省略break后还会执行其他case的原因分析
请分析以下PHP代码的输出结果: $a= 'dog'; switch($a) { case 'cat': echo "\$a is cat"; case 'dog': echo & ...
随机推荐
- Django(17)orm查询操作
前言 查找是数据库操作中一个非常重要的技术.查询一般就是使用filter.exclude以及get三个方法来实现.我们可以在调用这些方法的时候传递不同的参数来实现查询需求.在ORM层面,这些查询条件都 ...
- C++ primer plus读书笔记——第1章 预备知识
第1章 预备知识 1. Ritchie希望有一种语言能将低级语言的效率.硬件访问能力和高级语言的通用性.可移植性融合在一起,于是他在旧语言的基础上开发了C语言. 2. 在C++获得一定程度的成功后,S ...
- OO随笔之魔鬼的第一单元——多项式求导
OO是个借助Java交我们面向对象的课,可是萌新们总是喜欢带着面向过程的脑子去写求导,然后就是各种一面(main)到底.各种方法杂糅,然后就是被hack的很惨. 第一次作业:萌新入门面向对象 题目分析 ...
- 前端Node的实用方法
Node 一.什么是Node Node是以基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动.非阻塞式I/O模型(I/O是 input/output的缩写,即输入输出端口,在 ...
- [c++] 内存与变量
内存 程序代码区用来保存指令,常量区.全局数据区.堆.栈都用来保存数据 常量区和全局数据区有时也被合称为静态数据区,意思是这段内存专门用来保存数据,在程序运行期间一直存在 函数被调用时,会将参数.局部 ...
- Linux查看登录日志 last命令 查看当前登录用户
Linux查看登录日志 linux 发布于 31 分钟前 lastlog 打印系统账号最近一次的登录记录情况,解析的是/var/log/lastlog文件,它是一个data file类型的文件,文 ...
- 【转载】在Linux系统下用dd命令制作ISO镜像U盘启动盘
#### 将U盘插入USB接口 #umount /dev/sdb* #dd if=/iso存放路径/XXX.iso of=/dev/sdb bs=1M ##### [转载]在Linux系统下用dd命令 ...
- 012.Ansible高级特性
一 本地执行 如果希望在控制主机本地运行一个特定的任务,可以使用local_action语句. 假设我们需要配置的远程主机刚刚启动,如果我们直接运行playbook,可能会因为sshd服务尚未开始监听 ...
- Phoenix 使用教程
引言 hbase 提供很方便的 shell 脚本,可以对数据表进行 CURD 操作,但是毕竟是有一定的学习成本的,基本上对于开发来讲,sql 语句都是看家本领,那么,有没有一种方法可以把 sql 语句 ...
- 『动善时』JMeter基础 — 24、JMeter中使用“用户参数”实现参数化
目录 1.用户参数组件介绍 2.用户参数界面介绍 3.使用"用户参数"组件实现参数化 (1)测试计划内包含的元件 (2)线程组元件内容 (3)HTTP请求组件内容 (4)用户参数组 ...