跳到内容

第 1 卷 / 第 4 章 / 第 2 课

4.2 switch 语句与表达式 ​

前置知识:条件分支和 Java 17 基础语法

预计时间:30 分钟

完成标志:能区分传统冒号分支、箭头分支和 switch 表达式

传统 switch 语句 ​

java
String item = "book";

switch (item) {
    case "book":
        System.out.println("50 credits");
        break;
    case "magazine":
        System.out.println("40 credits");
        break;
    case "notebook":
        System.out.println("35 credits");
        break;
    default:
        System.out.println("unknown item");
}

本页采用 Java 17 的正式语法,不启用预览特性。switch 选择器支持 byte、short、char、int 及对应包装类型、枚举和 String。不支持 long、float、double 或 boolean。包装类型、枚举或 String 选择器为 null 时都会抛 NullPointerException;箭头写法和 default 也不会接住它,应在进入前明确处理。

冒号分支会穿透 ​

传统 case: 从匹配位置开始,若未遇到 break、return、throw 或 switch 结束,会继续执行后面的语句:

java
int day = 6;

switch (day) {
    case 6:
        System.out.println("Saturday");
    case 7:
        System.out.println("weekend");
        break;
    default:
        System.out.println("weekday");
}

当 day 为 6,会打印两行。这可能是故意共享逻辑,也可能是漏写 break。显式注释有意穿透,并用测试锁定行为。

共享多个标签无需空分支复制代码:

java
switch (day) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        System.out.println("weekday");
        break;
    case 6:
    case 7:
        System.out.println("weekend");
        break;
    default:
        throw new IllegalArgumentException("day: " + day);
}

箭头分支不穿透 ​

Java 14 起正式提供箭头形式,Java 17 可直接使用:

java
switch (item) {
    case "book" -> System.out.println("50 credits");
    case "magazine" -> System.out.println("40 credits");
    case "notebook" -> System.out.println("35 credits");
    default -> System.out.println("unknown item");
}

一个分支需要多条语句时使用块:

java
case "book" -> {
    recordSale(item);
    System.out.println("50 credits");
}

多个常量可写在同一标签:

java
case "book", "magazine" -> System.out.println("physical item");

switch 表达式返回值 ​

java
int price = switch (item) {
    case "book" -> 50;
    case "magazine" -> 40;
    case "notebook" -> 35;
    default -> throw new IllegalArgumentException("item: " + item);
};

这是表达式,末尾声明语句需要分号。每条正常路径必须产生兼容结果;用 throw 的路径突然结束,不需要结果值。

块分支通过 yield 提供值:

java
int price = switch (item) {
    case "book" -> {
        int base = 50;
        System.out.println("featured item");
        yield base;
    }
    case "magazine" -> 40;
    default -> 0;
};

yield 结束当前 switch 表达式并提供结果,不等同于从整个方法 return。

穷尽性和 default ​

switch 表达式必须满足编译器的穷尽性检查;这不意味着 null 也被兜底。对开放的整数或字符串集合通常需要 default。对枚举穷尽列出所有常量时,编译器可判断覆盖完整;是否仍写 default 取决于你希望新枚举常量在重新编译时触发编译失败,还是走显式兜底。

不要用 default -> 0 隐藏本应拒绝的未知值。价格、权限、协议状态等场景通常更适合抛出明确异常或返回能表达缺失的类型。

何时选择 switch ​

  • 一个离散值映射到固定分支:适合 switch;
  • 范围、多个独立条件或复杂谓词:if / else if 更自然;
  • 分支不断增长且每个分支包含大量行为:考虑枚举多态、映射表或策略对象,而不是无限扩展 switch。

Python 与 C++ 边界 ​

Python 3.10+ 的 match 是结构化模式匹配,能力和语义不只是 Java switch 的改名,且没有传统穿透。

C++ switch 通常使用整数或枚举选择器,冒号分支也会穿透;它没有 Java 的 String switch,也没有 Java 17 的 switch 表达式箭头与 yield 语义。

专业速查 ​

需求Java 17 选择
只执行分支动作switch 语句
计算一个值switch 表达式
避免穿透箭头标签
多条语句并返回值箭头块 + yield
未知值应失败default -> throw ...
selector 可能为 nullswitch 前显式处理

离开这一页前 ​

给 "book"、未知商品和 null 各安排一条路径,再解释冒号分支何时穿透、箭头分支为何不需要 break,以及 yield 只把值交给 switch 表达式而不是退出整个方法。最后确认选择器不是 long 或 boolean。

下一节把一次分支选择扩展为重复处理十项交付记录。

→ 下一步:while、for 与遍历

规范入口:JLS §14.11:switch 语句、§15.28:switch 表达式。

Built with VitePress | Software Systems Atlas