分支结构:if 与 switch | JavaSE

分支结构:if 与 switch

一、学习目标

完成本章后,你应该能够:

  • 能够解释顺序结构、分支结构与循环结构各自解决什么问题。
  • 能够熟练使用 ifif-elseif-else if-else
  • 能够设计互斥、完整、不遗漏的区间条件。
  • 能够发现 if (...) ; 等典型控制流错误。
  • 能够使用传统 switch 完成离散值匹配。
  • 能够解释 casebreakdefault 与传统 switch 穿透。
  • 能够根据业务场景选择 ifswitch
  • 能够认识 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 知识问答

  1. 顺序、分支、循环三种结构分别解决什么问题?
  2. if 条件最终必须是什么类型?
  3. if 有哪三种常见形式?
  4. 多分支 if 为什么需要关注条件顺序?
  5. 为什么区间条件需要考虑重叠和遗漏?
  6. if (...) ; 为什么危险?
  7. 工程代码为什么通常建议保留 if 大括号?
  8. switch 的核心思想是什么?
  9. casebreakdefault 分别有什么作用?
  10. 什么叫 switch 穿透?
  11. 哪些情况下传统穿透曾经可以用于合并重复逻辑?
  12. if 与 switch 各自更适合什么业务?
  13. case -> 与传统 case: 最明显的控制流区别是什么?
  14. 什么是 switch expression?
  15. yield 在 switch expression 中解决什么问题?
  16. 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("优秀");
        }
    }
}

要求:

  1. 预测输出。
  2. 判断是否存在语法错误。
  3. 判断业务错误在哪里。
  4. 重新设计条件顺序。

继续:

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");
        }
    }
}

要求:

  1. 不运行预测输出。
  2. 解释传统 switch 的控制流。
  3. 指出哪些位置如果添加 break 会改变结果。

7.3 手写代码

  1. 判断一个整数是正数、负数还是 0。
  2. 输入成绩并输出等级,同时处理非法成绩。
  3. 判断年龄属于未成年、青年、中年或老年,并明确边界。
  4. 输入三个整数并输出最大值。
  5. 根据 1~7 输出星期。
  6. 使用传统 switch 实现 + - * / 四则运算选择。
  7. 使用现代 case -> 重写星期程序。
  8. 使用现代 switch expression 返回 "工作日""周末"

7.4 Debug

分析:

int age = 10;

if (age >= 18); {
    System.out.println("允许进入");
}

要求:

  1. 判断代码能否编译。
  2. 找出真正的逻辑问题。
  3. 解释分号在这里代表什么。

继续:

int score = 95;

if (score >= 60) {
    System.out.println("及格");
} else if (score >= 80) {
    System.out.println("良好");
} else if (score >= 90) {
    System.out.println("优秀");
}

要求:

  1. 找出业务逻辑问题。
  2. 不修改等级标准,重新排列条件。
  3. 思考非法成绩应该在哪里处理。

7.5 综合训练

开发一个控制台“简易自助服务菜单”。

菜单:

1. 查询账户
2. 存款
3. 取款
4. 修改密码
5. 退出

要求:

  1. 使用 Scanner 获取菜单编号。
  2. 使用 switch 分发功能。
  3. 对非法菜单编号进行处理。
  4. 至少一个功能内部再使用 if 判断业务条件。
  5. 可以先只打印“进入某功能”,不要求实现真实银行业务。
  6. 分别尝试传统 switch 和现代 case -> 两种写法。
  7. 比较哪一种在该场景下更加清晰。

7.6 本章验收

关闭资料后确认:

  • [ ] 能独立写三种 if 形式。
  • [ ] 能解释 if 为什么必须使用 boolean 条件。
  • [ ] 能正确设计区间边界。
  • [ ] 能识别 if (...); 错误。
  • [ ] 能解释多分支条件顺序。
  • [ ] 能从零写传统 switch。
  • [ ] 能解释 break、default 和穿透。
  • [ ] 能根据业务选择 if 或 switch。
  • [ ] 能使用 case ->
  • [ ] 能使用多个 case 标签。
  • [ ] 能写一个简单 switch expression。
  • [ ] 知道 Java 21 的 switch 已支持模式匹配,但不会在尚未学习面向对象时强行深入。

如果目前只会背:

if 条件
switch 值

却无法把真实业务规则设计成:

完整
互斥
无遗漏
易维护

的条件结构,那么分支结构仍未真正掌握。