分支结构:if 与 switch | JavaSE
分支结构:if 与 switch
一、学习目标
完成本章后,你应该能够:
- 能够解释顺序结构、分支结构与循环结构各自解决什么问题。
- 能够熟练使用
if、if-else和if-else if-else。 - 能够设计互斥、完整、不遗漏的区间条件。
- 能够发现
if (...) ;等典型控制流错误。 - 能够使用传统
switch完成离散值匹配。 - 能够解释
case、break、default与传统 switch 穿透。 - 能够根据业务场景选择
if或switch。 - 能够认识 JDK 21 中现代
switch的->、多标签和 switch expression。 - 能够知道 Java 21 已支持 pattern matching for switch,并理解它与传统常量匹配 switch 的区别。
二、核心知识
2.1 程序的三种基本控制结构
程序执行逻辑通常可以抽象成:
顺序结构
分支结构
循环结构
顺序结构
代码按照先后顺序执行:
System.out.println("第一步");
System.out.println("第二步");
System.out.println("第三步");
执行:
第一步
↓
第二步
↓
第三步
分支结构
根据条件决定:
哪段代码应该执行。
例如:
成绩 >= 60
├── true → 及格
└── false → 不及格
Java 主要使用:
if
switch
实现分支。
循环结构
用于:
重复执行一段代码。
例如:
打印 100 次
遍历数据
重复输入
将在下一章系统学习。
2.2 if 单分支
基本格式:
if (条件表达式) {
代码;
}
例如:
int age = 20;
if (age >= 18) {
System.out.println("成年人");
}
执行过程:
判断 age >= 18
↓
true?
/ \
yes no
↓ ↓
执行 跳过
2.3 if 条件必须产生 boolean
Java 的 if 条件必须能够得到:
boolean
即:
true / false
例如:
if (age >= 18) {
}
其中:
age >= 18
结果是 boolean。
而不能像某些语言一样写:
if (1) {
}
Java 不会自动把:
1
理解为 true。
这是 Java 强类型设计的重要体现。
2.4 if-else 双分支
如果需要二选一:
if (条件) {
条件成立时执行;
} else {
条件不成立时执行;
}
例如:
int score = 59;
if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
两个分支:
score >= 60
↓
true ───────── false
↓ ↓
及格 不及格
两边只会选择其中一条路径。
2.5 多分支 if
当业务存在多个区间时:
if (条件1) {
...
} else if (条件2) {
...
} else if (条件3) {
...
} else {
...
}
例如:
int score = 95;
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
执行规则是:
从上往下判断,一旦找到第一个为
true的分支,执行后整个 if-else-if 链结束。
所以:
条件顺序非常重要
2.6 为什么条件顺序会改变结果
观察:
int score = 95;
if (score >= 60) {
System.out.println("及格");
} else if (score >= 90) {
System.out.println("优秀");
}
95 同时满足:
score >= 60
score >= 90
但程序先遇到:
score >= 60
已经为:
true
于是输出:
及格
后面的:
score >= 90
不会继续判断。
这不是 Java 语法错误,而是:
业务条件设计错误。
正确设计区间时要考虑:
是否重叠
是否遗漏
判断顺序是否正确
边界是否明确
2.7 从严格条件向宽松条件排列
对于成绩:
>= 90
>= 80
>= 60
一般适合从:
限制最严格
向:
限制更宽松
排列。
例如:
if (score >= 90) {
} else if (score >= 80) {
} else if (score >= 60) {
} else {
}
这样前面的分支筛选掉更高等级后,后续条件才能自然表达剩余区间。
2.8 先处理非法数据
例如成绩的合法范围:
0 ~ 100
可以先处理:
if (score < 0 || score > 100) {
System.out.println("成绩非法");
} else if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
这种设计体现:
先排除非法情况
↓
再处理正常业务
后续项目开发会大量使用这种思路。
2.9 if 大括号
Java 允许某些情况下省略单条语句的大括号:
if (age >= 18)
System.out.println("成年人");
但工程代码通常更推荐:
if (age >= 18) {
System.out.println("成年人");
}
原因不是:
不加大括号一定错误。
而是:
更清晰
更容易扩展
更少维护错误
例如未来增加:
System.out.println("允许注册");
如果没有大括号,很容易误以为两句都受 if 控制。
2.10 一个危险错误:if 后多写分号
观察:
if (score >= 60); {
System.out.println("及格");
}
这里:
;
本身就是一条空语句。
所以真正的结构近似:
if (score >= 60) {
// 空语句
}
{
System.out.println("及格");
}
于是后面的代码块可能无论条件真假都执行。
这类错误最危险的地方在于:
代码可能可以编译,但业务逻辑完全错误。
2.11 if 嵌套
if 内部还可以继续放 if:
if (loggedIn) {
if (isAdmin) {
System.out.println("进入后台");
}
}
表示:
先确认已经登录
↓
再确认是不是管理员
但是嵌套过深会导致:
代码难读
逻辑复杂
维护困难
以后会逐步学习:
提前 return
合理组合条件
拆分方法
等方式降低嵌套复杂度。
三、使用方法
3.1 判断成年人
int age = 20;
if (age >= 18) {
System.out.println("成年人");
}
3.2 判断奇偶
int number = 10;
if (number % 2 == 0) {
System.out.println("偶数");
} else {
System.out.println("奇数");
}
3.3 成绩等级
int score = 88;
if (score < 0 || score > 100) {
System.out.println("非法成绩");
} else if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
3.4 判断三个整数中的最大值
int a = 10;
int b = 30;
int c = 20;
int max;
if (a >= b && a >= c) {
max = a;
} else if (b >= a && b >= c) {
max = b;
} else {
max = c;
}
System.out.println(max);
这里最重要的不是答案,而是学会:
把自然语言业务条件转换成布尔表达式。
3.5 什么是 switch
switch 适合根据:
一个表达式得到的值
在多个候选分支之间进行选择。
传统写法:
switch (表达式) {
case 值1:
代码;
break;
case 值2:
代码;
break;
default:
代码;
}
例如:
int day = 1;
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
default:
System.out.println("非法数字");
}
3.6 传统 switch 的执行过程
例如:
switch (day) {
case 1:
...
break;
case 2:
...
break;
default:
...
}
可以理解为:
计算 day
↓
得到一个值
↓
与各 case 匹配
↓
找到对应 case
↓
开始执行
↓
遇到 break
↓
离开 switch
如果没有任何 case 匹配:
default
可以负责兜底逻辑。
3.7 break 的作用
在传统 case: switch 中:
break;
表示:
立即结束当前 switch。
例如:
case 1:
System.out.println("星期一");
break;
匹配 case 1 后,执行完:
星期一
遇到 break:
直接退出 switch
不会继续执行后面的 case 代码。
3.8 什么是 switch 穿透
如果传统 switch 中没有 break:
int day = 1;
switch (day) {
case 1:
System.out.println("A");
case 2:
System.out.println("B");
case 3:
System.out.println("C");
}
匹配:
case 1
之后程序开始执行。
由于没有 break,会继续进入后续语句:
A
B
C
这种现象通常称为:
switch fall-through,常译作 switch 穿透。
3.9 穿透不一定永远是错误
有时候多个 case 本来就应该执行相同代码:
String day = "周六";
switch (day) {
case "周六":
case "周日":
System.out.println("周末");
break;
default:
System.out.println("工作日");
}
这里:
周六
周日
都进入相同业务逻辑。
传统语法可以利用穿透减少重复。
不过 JDK 21 中还有更加清晰的现代写法。
3.10 default
default 表示:
没有任何其他 case 匹配时执行的默认分支。
例如:
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
default:
System.out.println("非法");
}
default 并不是所有 switch 都在语法上必须写。
但是如果业务存在:
非法输入
未知状态
其他情况
通常应该认真考虑默认处理。
3.11 case 不能出现重复常量
例如:
switch (number) {
case 1:
break;
case 1:
break;
}
同一个常量分支重复,会造成编译错误。
因为编译器无法接受两个完全相同的常量匹配分支。
3.12 case 不只是“只能写字面量”
传统入门材料经常写:
case 后只能写字面量。
更准确地说,传统常量 case 使用的是:
编译期可以确定的 case 常量表达式,或者相应的枚举常量等。
例如:
final int MONDAY = 1;
switch (day) {
case MONDAY:
System.out.println("星期一");
break;
}
这种编译期常量可以合法使用。
而普通运行时变量:
int monday = 1;
不能直接被当作传统 case 常量:
case monday:
所以应该理解:
case 常量
≠
只能写裸数字字面量
3.13 if 与 switch 应该如何选择
不要机械记:
switch 一定比 if 快
真正的主要选择依据应该是:
业务语义和代码可读性。
更适合 if
区间:
score >= 90
age >= 18 && age <= 60
price >= 4000 && price <= 6000
复杂布尔组合:
isLogin && !locked && score > 80
更适合 switch
对一个值进行离散匹配:
星期
菜单编号
状态码
命令
枚举状态
例如:
1 → 查询
2 → 新增
3 → 修改
4 → 删除
核心判断:
区间 / 复杂条件
→ if
离散值 / 多候选分支
→ switch 往往更清晰
四、原理与进阶
4.1 JDK 21 中不要只会传统 switch
现代 Java 的 switch 已经比早期 Java 强大很多。
Java 21 中可以使用:
switch (day) {
case 1 -> System.out.println("星期一");
case 2 -> System.out.println("星期二");
default -> System.out.println("非法");
}
这里使用:
->
而不是传统:
:
它的重要特点之一是:
不依赖传统 fall-through。
因此普通分支不需要再机械写:
break;
4.2 一个 case 匹配多个值
现代 switch 可以:
switch (day) {
case 1, 2, 3, 4, 5 ->
System.out.println("工作日");
case 6, 7 ->
System.out.println("周末");
default ->
System.out.println("非法");
}
相较于传统:
case 1:
case 2:
case 3:
...
更加直观。
4.3 switch expression
现代 Java 的 switch 不仅能作为:
Statement
还可以成为:
Expression。
例如:
String result = switch (day) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "周末";
default -> "非法";
};
这里整个:
switch (...)
会产生一个值。
于是:
输入 day
↓
switch
↓
得到 String
↓
保存到 result
这可以避免传统代码:
String result;
switch (...) {
...
result = ...;
}
中的重复赋值。
4.4 yield
当 switch expression 的某一个分支需要执行多条语句再返回结果时,可以使用代码块和:
yield
例如:
String result = switch (day) {
case 1, 2, 3, 4, 5 -> {
System.out.println("准备工作");
yield "工作日";
}
case 6, 7 -> "周末";
default -> "非法";
};
这里:
yield "工作日";
表示:
为当前 switch expression 提供结果。
它与方法中的:
return
不是同一个关键字职责。
4.5 Java 21 的 pattern matching for switch
Java 21 已经正式支持:
Pattern Matching for switch。
例如:
static String describe(Object value) {
return switch (value) {
case Integer i -> "整数:" + i;
case String s -> "字符串:" + s;
case null -> "空值";
default -> "其他类型";
};
}
这时候 switch 已经不只是:
值 == 某个常量?
还可以表达:
这个对象是不是某种类型?
甚至可以结合:
when
添加守卫条件。
例如:
case String s when s.isEmpty() -> "空字符串";
这一能力会涉及:
引用类型
继承
多态
模式匹配
因此当前章节只建立认识,不需要立刻深入。
后续学完面向对象后再看,会容易很多。
4.6 原教学资料中的 switch 类型限制为什么需要更新
早期传统 switch 教学常写:
byte
short
int
char
enum
String
并强调:
不支持 long、float、double
这对于传统常量 switch 的基础认识具有历史教学价值。
但 Java 21 已正式加入 pattern matching for switch,selector 的能力扩展到了引用类型模式。
因此学习时应该区分:
传统常量 switch
和:
Java 21 pattern switch
不能把旧版限制当成现代 Java 21 的完整规则。
4.7 if 条件设计比 if 语法本身更重要
下面语法完全正确:
if (score >= 60) {
System.out.println("及格");
} else if (score >= 90) {
System.out.println("优秀");
}
但业务是错误的。
因此真正困难的不是:
if 后面写括号
而是:
把现实规则转换成
完整、互斥、无遗漏的逻辑条件
这是程序设计能力的开始。
五、实践应用
5.1 用户权限
if (!loggedIn) {
System.out.println("请先登录");
} else if (isAdmin) {
System.out.println("进入管理员后台");
} else {
System.out.println("进入用户主页");
}
5.2 商品价格区间
if (price < 1000) {
System.out.println("入门档");
} else if (price < 3000) {
System.out.println("中端");
} else {
System.out.println("高端");
}
5.3 菜单功能
switch (command) {
case 1 -> System.out.println("查询");
case 2 -> System.out.println("新增");
case 3 -> System.out.println("修改");
case 4 -> System.out.println("删除");
default -> System.out.println("非法命令");
}
这里离散值非常明确,因此 switch 比一长串:
if (command == 1) {
} else if (command == 2) {
}
更能表达:
根据命令值选择功能。
六、常见问题
6.1 if 条件能不能写整数?
不能直接写:
if (1) {
}
Java if 条件需要得到 boolean。
6.2 为什么 if (...); 很危险?
因为分号构成空语句。
真正受 if 控制的可能只是:
什么都不做
后面的代码块反而独立执行。
6.3 if 能不能不写大括号?
单条语句情况下语法允许省略。
但工程代码通常推荐保留大括号,提高可维护性。
6.4 多分支 if 为什么要关注条件顺序?
因为:
第一个满足条件的分支执行后,后续
else if不再判断。
所以宽泛条件放在前面可能吞掉更加精确的分支。
6.5 default 必须写吗?
不是所有 switch 在语法上都必须存在 default。
但从业务完整性角度,应考虑:
如果没有任何 case 匹配,程序应该怎么办?
对于 switch expression 还需要满足结果完整性等相应规则。
6.6 传统 switch 为什么容易忘记 break?
传统:
case ...:
支持 fall-through。
没有 break 时会继续向后执行。
如果业务不需要穿透,就必须注意退出控制。
现代:
case ... ->
可以显著降低这类错误。
6.7 Java 21 还应该故意利用 switch 穿透吗?
如果维护的是传统 case: 代码,理解穿透仍然必须。
但新代码如果只是:
多个值对应同一个结果
通常优先考虑:
case 1, 2, 3 -> ...
比依赖隐式 fall-through 更直观。
6.8 case 后真的只能写字面量吗?
不准确。
传统常量 case 可以使用满足规范要求的:
常量表达式
枚举常量
等。
普通运行时变量则不能直接充当传统 case 常量。
6.9 switch 一定比 if 性能好吗?
不应该以这种绝对说法进行技术选型。
编译器和 JVM 可以针对不同分支形式采用不同实现和优化。
日常业务首先应该根据:
语义
可读性
维护成本
条件结构
选择。
不要为了所谓“switch 一定更快”而把不适合 switch 的区间业务强行改写。
6.10 区间判断为什么更适合 if?
例如:
score >= 90
price > 4000 && price <= 6000
本质是:
布尔条件。
而传统常量 switch 更自然地表达:
值 A
值 B
值 C
因此区间和复杂组合条件通常使用 if 更直观。
七、练习与验收
7.1 知识问答
- 顺序、分支、循环三种结构分别解决什么问题?
if条件最终必须是什么类型?if有哪三种常见形式?- 多分支 if 为什么需要关注条件顺序?
- 为什么区间条件需要考虑重叠和遗漏?
if (...) ;为什么危险?- 工程代码为什么通常建议保留 if 大括号?
- switch 的核心思想是什么?
case、break、default分别有什么作用?- 什么叫 switch 穿透?
- 哪些情况下传统穿透曾经可以用于合并重复逻辑?
- if 与 switch 各自更适合什么业务?
case ->与传统case:最明显的控制流区别是什么?- 什么是 switch expression?
yield在 switch expression 中解决什么问题?- Java 21 pattern switch 相比传统 switch 扩展了什么能力?
7.2 代码阅读
禁止运行:
public class IfRead01 {
public static void main(String[] args) {
int score = 95;
if (score >= 60) {
System.out.println("及格");
} else if (score >= 90) {
System.out.println("优秀");
}
}
}
要求:
- 预测输出。
- 判断是否存在语法错误。
- 判断业务错误在哪里。
- 重新设计条件顺序。
继续:
public class SwitchRead01 {
public static void main(String[] args) {
int number = 1;
switch (number) {
case 1:
System.out.println("A");
case 2:
System.out.println("B");
default:
System.out.println("C");
}
}
}
要求:
- 不运行预测输出。
- 解释传统 switch 的控制流。
- 指出哪些位置如果添加
break会改变结果。
7.3 手写代码
- 判断一个整数是正数、负数还是 0。
- 输入成绩并输出等级,同时处理非法成绩。
- 判断年龄属于未成年、青年、中年或老年,并明确边界。
- 输入三个整数并输出最大值。
- 根据 1~7 输出星期。
- 使用传统 switch 实现
+ - * /四则运算选择。 - 使用现代
case ->重写星期程序。 - 使用现代 switch expression 返回
"工作日"或"周末"。
7.4 Debug
分析:
int age = 10;
if (age >= 18); {
System.out.println("允许进入");
}
要求:
- 判断代码能否编译。
- 找出真正的逻辑问题。
- 解释分号在这里代表什么。
继续:
int score = 95;
if (score >= 60) {
System.out.println("及格");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 90) {
System.out.println("优秀");
}
要求:
- 找出业务逻辑问题。
- 不修改等级标准,重新排列条件。
- 思考非法成绩应该在哪里处理。
7.5 综合训练
开发一个控制台“简易自助服务菜单”。
菜单:
1. 查询账户
2. 存款
3. 取款
4. 修改密码
5. 退出
要求:
- 使用 Scanner 获取菜单编号。
- 使用 switch 分发功能。
- 对非法菜单编号进行处理。
- 至少一个功能内部再使用 if 判断业务条件。
- 可以先只打印“进入某功能”,不要求实现真实银行业务。
- 分别尝试传统 switch 和现代
case ->两种写法。 - 比较哪一种在该场景下更加清晰。
7.6 本章验收
关闭资料后确认:
- [ ] 能独立写三种 if 形式。
- [ ] 能解释 if 为什么必须使用 boolean 条件。
- [ ] 能正确设计区间边界。
- [ ] 能识别
if (...);错误。 - [ ] 能解释多分支条件顺序。
- [ ] 能从零写传统 switch。
- [ ] 能解释 break、default 和穿透。
- [ ] 能根据业务选择 if 或 switch。
- [ ] 能使用
case ->。 - [ ] 能使用多个 case 标签。
- [ ] 能写一个简单 switch expression。
- [ ] 知道 Java 21 的 switch 已支持模式匹配,但不会在尚未学习面向对象时强行深入。
如果目前只会背:
if 条件
switch 值
却无法把真实业务规则设计成:
完整
互斥
无遗漏
易维护
的条件结构,那么分支结构仍未真正掌握。