在Java里处理多个固定值的分支判断,switch是最直接的控制语句之一。它接收一个表达式,把结果和若干个case常量做等值比较,匹配成功后执行对应代码。和一连串if else相比,switch在语义上更聚焦“同一个变量的不同取值”,编译器和阅读者都能更快理解意图。

传统switch的基础写法
最经典的写法由switch关键字、括号里的整数兼容类型或字符串表达式、若干case标签、break以及default组成。每个case后面跟常量,冒号之后是语句块。若省略break,程序会继续执行下一个case,这称为“穿透”,有时是bug来源,有时却被故意用来合并多个分支。
下面例子根据数字星期输出中文名称,注意break的位置。如果值是1到5都走工作日提示,就可以利用穿透省掉重复代码,但要在最后一个工作日case前补逻辑。
public class SwitchDemo {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("工作日");
break;
case 6:
case 7:
System.out.println("周末");
break;
default:
System.out.println("非法日期");
}
}
}
上面的代码把1到5的case都穿透到同一段输出,减少了冗余。default相当于else,当没有任何case匹配时执行,通常放在末尾,但语法上可以出现在任意位置,只是放末尾可读性更好。
需要强调的是,case标签必须是编译期常量,不能是变量或运行期才能确定的值。早期Java版本不支持long、float、double以及boolean作为switch表达式类型,字符串和枚举是后来加入的。
支持的数据类型与字符串用法
Java 7开始switch支持String,底层是用hashCode和equals实现的,所以比较的是内容而非引用。在命令分发、协议解析时非常方便。枚举则更安全,因为候选值被限定在类型里。
以下代码展示字符串分支,注意字符串可能为null,直接传入switch会抛NullPointerException,需要提前判断。
public class StringSwitch {
public static void main(String[] args) {
String cmd = "start";
if (cmd == null) {
System.out.println("命令为空");
return;
}
switch (cmd) {
case "start":
System.out.println("启动");
break;
case "stop":
System.out.println("停止");
break;
default:
System.out.println("未知命令");
}
}
}
使用字符串时,编译器会生成基于哈希的查找,性能接近整数switch,但常数因子略大。如果分支极多且对性能敏感,可考虑用枚举或Map预先映射。
枚举版本如下,类型系统保证了不会传错值,IDE也能自动补全case。
enum Status { RUNNING, STOPPED, PENDING }
public class EnumSwitch {
public static void main(String[] args) {
Status s = Status.RUNNING;
switch (s) {
case RUNNING:
System.out.println("运行中");
break;
case STOPPED:
System.out.println("已停止");
break;
case PENDING:
System.out.println("等待中");
break;
}
}
}
Java 12之后的新语法
传统switch容易漏写break,新版本引入了箭头规则(case L -> 语句),匹配后只执行箭头右侧内容,不会穿透。还可以作为表达式返回值,用yield关键字给出结果。
箭头写法让分支和结果绑定得更紧,代码更短。下面例子用switch表达式给分数评级,整个switch外围能直接接收一个值。
public class SwitchExpr {
public static void main(String[] args) {
int score = 85;
String level = switch (score / 10) {
case 10, 9 -> "优秀";
case 8, 7 -> "良好";
case 6 -> "及格";
default -> "不及格";
};
System.out.println(level);
}
}
如果分支需要多行语句,可以用花括号并在里面用yield返回。yield和return不同,它只从switch表达式退出并送回值,不会退出整个方法。
public class SwitchYield {
public static void main(String[] args) {
int n = 2;
int result = switch (n) {
case 1 -> {
System.out.println("处理1");
yield 10;
}
case 2 -> {
System.out.println("处理2");
yield 20;
}
default -> {
System.out.println("其他");
yield 0;
}
};
System.out.println(result);
}
}
新语法在代码审查时更不容易出错,因为编译器强制要求覆盖所有可能情况或写default,否则报错误。对多分支且需返回值的场景,优先使用表达式形态。
常见误区与避坑
一个典型错误是认为switch只能用在简单场景,其实它可以嵌套,也可以在case里调用方法。但要小心变量作用域:在case里声明变量必须加花括号,否则多个case共享作用域会导致重定义编译错误。
下面这种写法会编译失败,因为a在两个case里都被声明,改成带块的形式即可。
public class ScopeBug {
public static void main(String[] args) {
int x = 1;
switch (x) {
case 1: {
int a = 10;
System.out.println(a);
break;
}
case 2: {
int a = 20;
System.out.println(a);
break;
}
}
}
}
另一个坑是long类型不能直接用于switch,有人会强制转成int,但可能溢出。若确实需要基于长整型做分发,建议用if else或Map<Long, Runnable>。
在团队代码中,如果分支超过十个且逻辑复杂,switch可读性也会下降。此时可把每个分支抽成独立方法,或改用策略模式,让扩展不修改原方法。
性能与选型建议
对整数分支,编译器可能生成tableswitch或lookupswitch指令。前者用值直接索引,适合密集连续值;后者用二分查找,适合稀疏值。通常不用手动干预,但了解原理有助于解释为什么小范围连续枚举很快。
选型上,等值匹配且分支多于三个,选switch;范围判断、复杂布尔组合,用if else;需要动态注册分支,用Map或策略。下表简要对比:
| 场景 | 推荐结构 | 理由 |
|---|---|---|
| 固定枚举值分发 | switch表达式 | 类型安全、代码短 |
| 区间判断 | if else | switch不支持范围 |
| 运行时扩展分支 | Map或策略模式 | 避免改原switch |
掌握传统写法与新表达式,理解穿透、default、yield和类型限制,你就能在Java里把多分支判断写得既清楚又稳妥。