这是一个非常常见且有用的特性,它让代码在处理多条件分支时更加清晰和简洁。
核心要点
Java 7 (JDK 7) 及更高版本开始,switch 语句可以直接支持 String 类型,在此之前,switch 只能用于 byte, short, char, int 以及它们的包装类,以及 Java 5 引入的 enum 枚举类型。
基本语法
switch 语句的语法结构如下:
switch (expression) {
case value1:
// 当 expression 的值等于 value1 时执行的代码
break;
case value2:
// 当 expression 的值等于 value2 时执行的代码
break;
// 可以有任意数量的 case
default:
// 当 expression 的值不匹配任何一个 case 时执行的代码
break; // default 中的 break 是可选的,但推荐加上
}
对于字符串 String,expression 就是一个字符串变量,而 value1, value2 等就是你要匹配的字符串常量。
代码示例
下面是一个简单的例子,根据用户输入的星期几,输出对应的中文描述。
public class StringSwitchExample {
public static void main(String[] args) {
String dayOfWeek = "Monday";
switch (dayOfWeek) {
case "Monday":
System.out.println("星期一");
break;
case "Tuesday":
System.out.println("星期二");
break;
case "Wednesday":
System.out.println("星期三");
break;
case "Thursday":
System.out.println("星期四");
break;
case "Friday":
System.out.println("星期五");
break;
case "Saturday":
System.out.println("星期六");
break;
case "Sunday":
System.out.println("星期日");
break;
default:
System.out.println("无效的输入,请输入一个有效的星期名。");
break;
}
}
}
输出:
星期一
工作原理(非常重要)
很多人会好奇,switch 是如何高效地比较字符串的?它不是像 if-else 那样逐个进行 equals() 比较的吧?
Java 编译器在底层对 switch 字符串做了非常巧妙的优化,以避免多次调用 equals() 方法,从而提高性能。
其工作原理大致如下:
- 计算哈希码:
switch语句首先会计算switch表达式中字符串的哈希码(hashCode())。 - 哈希码匹配:它会将这个哈希码与每个
case标签的字符串常量的哈希码进行比较。- 如果哈希码不匹配,则该
case被直接跳过,效率很高。 - 如果哈希码匹配,这并不意味着字符串一定相等(因为哈希冲突是可能发生的)。
- 如果哈希码不匹配,则该
- 内容比较:对于哈希码匹配的
case,Java 会使用String.equals()方法来精确地比较字符串内容,以确保它们确实是相等的。
总结一下:switch 字符串利用了哈希码进行快速筛选,只对少数哈希码相同的候选者进行精确的 equals 比较,这比一长串 if-else if (str.equals("...")) 要高效得多,因为后者会对每个 if 分支都执行一次 equals 方法。
最佳实践和注意事项
在使用 switch 字符串时,有一些非常重要的规则和最佳实践:
a. case 标签必须是字符串常量
case 后面不能是变量,必须是编译时就能确定的字符串字面量。
// 错误示范
String myCase = "hello";
switch (str) {
// case myCase: // 编译错误!case 标签必须是常量
// ...
// break;
case "hello": // 正确
...
break;
}
b. null 值检查
如果传入的 switch 表达式可能为 null,必须先进行检查,否则会抛出 NullPointerException。
String status = null;
// 错误示范:会抛出 NullPointerException
// switch (status) { ... }
// 正确示范
if (status == null) {
System.out.println("状态为空");
} else {
switch (status) {
case "SUCCESS":
System.out.println("操作成功");
break;
case "FAILED":
System.out.println("操作失败");
break;
default:
System.out.println("未知状态");
break;
}
}
c. break 语句的重要性
在每个 case 块的末尾,通常需要使用 break 语句,如果没有 break,程序会继续执行下一个 case 块中的代码,这被称为 “贯穿” (fall-through)。
示例(贯穿现象):
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("水果是苹果");
// 注意:这里没有 break
case "banana":
System.out.println("水果是香蕉");
break;
default:
System.out.println("是其他水果");
}
输出:
水果是苹果
水果是香蕉
虽然贯穿在某些情况下可以故意使用,但它是一个常见的错误来源。强烈建议在每个 case 块的最后都加上 break,除非你明确地想要利用贯穿特性。
d. default 分支
default 分支是可选的,但推荐总是加上,以处理所有未预料到的情况,使代码更健壮。
switch vs. if-else if
什么时候应该用 switch 字符串,什么时候用 if-else if 呢?
| 特性 | switch 字符串 |
if-else if |
|---|---|---|
| 可读性 | 当有多个离散、明确的值需要匹配时,switch 结构更清晰,代码更整洁。 |
当条件比较复杂(如范围、逻辑组合 &&, )时,if-else if 更自然。 |
| 性能 | 通常更优,因为底层有哈希码优化。 | 对于简单比较,性能差异不大,但对于多个 equals() 调用,switch 更高效。 |
| 灵活性 | case 标签必须是常量。 |
条件可以是任何返回布尔值的表达式,非常灵活。 |
| 适用场景 | 枚举值、状态码、配置选项等离散值匹配。 | 范围检查、复合条件、空值检查等。 |
- 当你需要根据一个变量的多个固定值来执行不同的逻辑时,优先使用
switch字符串,它更符合代码的意图,也更易读。 - 当你的逻辑比较复杂,或者条件不是简单的等值匹配时,使用
if-else if。
Java 的 switch 字符串是一个强大且高效的功能,它极大地简化了多条件分支的代码。
记住三个关键点:
- 可用性:Java 7+ 才支持。
- 原理:底层通过
hashCode()进行快速筛选,再用equals()精确匹配,性能很高。 - 规范:
case后必须是常量,注意null检查,并且强烈建议每个case都以break。
