switch 分支
本教程共 100 篇 · 第 20 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
20. switch 分支
本节目标:当判断条件是一连串”等于某个固定值”的选择时,用 switch 比层层 if 更清爽。学会传统 switch 的写法、看懂穿透陷阱,并掌握 Java 14 起正式可用的 switch 表达式新语法。
if 擅长”大于小于”这类范围判断,可有些场景是”等于 A 怎么做、等于 B 怎么做”。比如游戏菜单选 1 单人、2 多人、3 退出,这种多路相等匹配,用 switch 更合适。
传统 switch 的写法
switch 后面跟一个表达式,JVM 拿它的结果去挨个比对 case,命中哪个就从这个 case 往下执行,直到遇到 break 才停。
public class Main {
public static void main(String[] args) {
int option = 1;
switch (option) {
case 1:
System.out.println("单人模式");
break;
case 2:
System.out.println("多人模式");
break;
case 3:
System.out.println("退出游戏");
break;
default:
System.out.println("无效选项");
}
}
}
```bash
`default` 是"以上都没命中"时的兜底,类似 `if` 里的最后一个 `else`。
## 穿透效应:别漏写 break
这是传统 `switch` 最容易翻车的地方。`case` 本身没有花括号,一旦命中,后面的语句会**一路执行下去**,这就是"穿透"。漏写 `break` 时:
```java
int option = 2;
switch (option) {
case 1: System.out.println("1");
case 2: System.out.println("2");
case 3: System.out.println("3");
default: System.out.println("other");
}
option = 2 会连着打印出 2、3、other。因为从 case 2 进入后没遇到 break,就势如破竹冲到底。
Warning任何时候都记得给每个
case写break。如果确实想让几个case共用一段逻辑(比如 2 和 3 都做同一件事),故意让它们”穿透”是可以的,但这种写法要加注释说明,免得别人(或一个月后的你)误以为是漏写了。
switch 能匹配什么类型
传统 switch 的表达式结果得是以下类型之一:
- 整数类:
byte、short、char、int(及它们的包装类) - 字符串
String - 枚举
enum
字符串匹配是比较”内容相等”,这点比 == 安全:
String fruit = "apple";
switch (fruit) {
case "apple": System.out.println("苹果"); break;
case "pear": System.out.println("梨"); break;
default: System.out.println("其他水果");
}
```bash
> [!NOTE]
> 注意 `long`、`float`、`double`、布尔值**不能**直接做 switch 的表达式。需要的话先转成能匹配的类型,或者改用 `if`。
## switch 表达式(Java 14 正式)
传统写法要手写 `break`、容易穿透,于是 Java 12 开始引入、到 **Java 14 正式定为标准特性**的 `switch` 表达式,用箭头 `->` 改写,自动保证只走匹配的那一支,再也不用 `break`:
```java
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("苹果");
case "pear" -> System.out.println("梨");
case "mango" -> {
System.out.println("芒果");
System.out.println("好选择");
}
default -> System.out.println("其他水果");
}
箭头后面如果有多条语句,用 {} 包起来。新语法没有穿透效应,命中即止。
用 switch 表达式直接给变量赋值
新语法的杀手锏,是它能当表达式用,直接返回一个值赋给变量:
String fruit = "pear";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2; // 多个值用逗号合并
default -> 0;
};
System.out.println("opt = " + opt);
```bash
对比传统写法要先用 `int opt;` 声明,再在每个 `case` 里赋值加 `break`,新语法干净太多了。
## yield:在分支里返回复杂结果
如果某个分支要算好几步才能得出返回值,用 `{}` 包起来,再用 `yield` 把值交出来:
```java
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // 把这个值作为整个 switch 的返回值
}
};
yield 的作用类似方法里的 return,只不过它返回的是 switch 表达式的值。
新旧写法怎么选
| 对比项 | 传统 switch(case:) | switch 表达式(->) |
|---|---|---|
| 引入版本 | Java 1.0 起 | Java 14 正式(12/13 预览) |
| 分支结束 | 必须手写 break | 自动结束 |
| 穿透风险 | 有,漏写 break 就穿 | 无 |
| 能否当值用 | 不能,只能在分支里赋值 | 能,int x = switch(...) {...}; |
| 多值合并 | 靠连写空 case 穿透 | 逗号分隔 case "a", "b" -> |
| 分支多行 | 直接写,靠 break 收尾 | 用 {} 包住,yield 交值 |
| 穷尽性检查 | 不检查 | 当值用时强制覆盖全部或带 default |
| 建议 | 历史兼容,不推荐新写 | 主线推荐 |
同一段逻辑,两种写法摆一起看得最清楚:
// 历史兼容写法,不推荐
int days;
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
default:
days = 28;
}
// Java 14 起的推荐写法
int days = switch (month) {
case 1, 3, 5, 7, 8, 10, 12 -> 31;
case 4, 6, 9, 11 -> 30;
default -> 28;
};
```bash
上面那段要先声明 `int days;`、每支赋值、每支 `break`,漏一个 `break` 结果就错;下面这段一次成型,`days` 还能直接写成 `final`。
> [!TIP]
> 新项目一律用 **switch 表达式(箭头语法)**,不用写 `break`、不会穿透、能直接赋值,既安全又简洁。传统 `case ... break` 写法当作"历史兼容"认识即可,读老代码时看得懂就行。
## 再往前一步:模式匹配 switch
Java 21 起,`switch` 的 `case` 还能直接匹配**类型**,不再局限于固定值:
```java
Object obj = 42;
String desc = switch (obj) {
case Integer i -> "整数 " + i;
case String s -> "字符串,长度 " + s.length();
default -> "其他类型";
};
case Integer i 的意思是「如果 obj 是 Integer,就把它转好类型装进变量 i」,省掉了以前 instanceof 加强制转换那两步。这块要配合多态和 sealed 类才好发挥,第 43、48 章会再碰到,现在知道 switch 已经进化到这一步就够了。
小结:switch 适合多路相等匹配;传统写法务必写 break 防穿透;Java 14 起的 switch 表达式用 ->、可 yield 返回值,是主线推荐写法。
动手练一练
用 switch 把”星期几”映射到”今天吃什么”:周一到周五食堂,周六火锅,周日外卖。先用传统 case + break 写一遍,体会漏写 break 时的”穿透”现象;再改成 Java 14 的 switch 表达式箭头写法,对比哪种更清爽。
Tip想验证穿透,故意删掉一个
break,输入那个 case 的值,看输出是不是”连着后面几个 case 一起打印了”。亲眼看到穿透,才真正理解为什么传统写法离不开break。
易错提醒
第一,传统 switch 忘了 break 会穿透,这是历史包袱,能写新表达式就别用老写法。第二,switch 的表达式在 Java 里支持的类型有限:早期只能 byte、short、char、int 和对应的包装类、enum、字符串;Java 之后还支持 long 吗?不支持——long 不能做 case。记不住就记住:常用就是 int、String、enum。第三,新表达式每个分支都要覆盖所有可能,或带 default,否则编译器报错。
拓展:switch 表达式的 yield 与枚举
Java 14 的 switch 表达式除了箭头 ->,还能用 yield 返回值:在大括号里写逻辑后 yield 值;。当某个分支要执行多行代码再返回时,yield 比箭头更合适。例如 case 1: { int x = calc(); yield x; }。
另外 switch 配合 enum 枚举类型非常搭:把”星期""状态”定义成枚举,用 switch 分支处理,编译器还能帮你检查是否漏了某个枚举值。记住 switch 支持的类型是有限的(int、String、enum 及少量兼容类型),long、浮点数、boolean 都不能做 case——需要这些时,老老实实用 if。
常见疑问
Q:什么时候用 switch,什么时候用 if?
判断是「等于某几个固定值」就用 switch,是「落在某个区间」就用 if。成绩分档要写 score >= 90,这是范围判断,switch 干不了;菜单选项 1/2/3 是相等匹配,switch 比三个 else if 清爽。
Q:case 后面能写变量吗?
不能,必须是编译期常量——字面量、final 常量或枚举值。写变量会报错:
error: constant expression required
case n -> "命中";
^
```text
要按变量比较,改用 `if`。
**Q:`switch` 表达式漏了分支会怎样?**
当值用时编译器强制要求穷尽。少写 `default` 又没覆盖全部可能:
```text
error: the switch expression does not cover all possible input values
对枚举来说,这条规则是白送的保险:以后往枚举里加了新常量,所有相关 switch 立刻编译报错,提醒你补上处理逻辑,不会静悄悄走到 default 里出错。
Q:case 里能不能不写 default?
作为语句用(不取值)可以省,命中不了就整段跳过。当表达式取值时不能省,除非分支已经覆盖了枚举的全部常量。