关于 Java switch 的一些理论知识
想象你是一名骑士,停在岔路口。如果向左走,则会失去马。如果向右走,则会获取知识。那么如何在代码中表示这种情况?你可能已经知道我们使用 if-then 和 if-then-else 等结构来做出这些决定。
if (turn_left) {
System.out.println("You will lose your horse");
}
if (turn_right) {
System.out.println("You will gain knowledge");
}
else
System.out.println("So you're just going to stand there?");
如果这条路不是分成两条,而是分成十条呢?则你的路有“完全靠右”、“稍微靠左”、“靠左一点”等,总共有 10 条可能的路?
想像一下你的“if-then-else”代码在这种情况下要增加多长!
if (option1)
{…}
else if (option2)
{…}
…
else if (optionN) ...
假设路上有一个 10 分叉(重要的是,选项的数量是有限的)。对于这种情况,Java 提供了 switch 语句。
switch (ExpressionForMakingAChoice) {
case (Value1):
Code1;
break;
case (Value2):
Code2;
break;
...
case (ValueN):
CodeN;
break;
default:
CodeForDefaultChoice;
break;
}
下面是该语句的工作方式:- 对 ExpressionForMakingAChoice 求值。然后 switch 语句将结果值与下一个 ValueX 进行比较(按列出的顺序)。
- 如果 ExpressionForMakingAChoice 与 ValueX 匹配,则执行冒号后面的代码。
- 如果遇到 break 语句,则将控制转移到 switch 语句之外。
- 如果 ExpressionForMakingAChoice 与任何 ValueX 都不匹配,则控制传递给 CodeForDefaultCase。
在 switch 语句中,ExpressionForMakingAChoice 的类型必须是以下之一:
- byte、short、char、int。
- Byte、Short、Character、Integer(原始数据类型的包装类)。
- String。
- Enum。
default 块是可选的。如果此块不存在并且 ExpressionForMakingAChoice 与任何 ValueX 都不匹配,则不会执行任何操作。
不需要 break 语句。如果此语句不存在,代码将继续执行(忽略 case 语句中的进一步比较),直到第一次出现 break 或直到 switch 语句结束。
如果需要为多个选择执行相同的代码,可以通过指定几个连续的 case 语句来消除重复项。
现在了解下如何在 Java 中使用 switch 语句
别担心:这个理论现在已经完善了。了解下面的例子后,你就理解得更明白。好吧,让我们开始。我们看一个涉及太阳系行星的天文学例子。 根据国际上最新的一致意见,冥王星因其轨道特性,被排除在几大行星之外。我们记得太阳系的行星按与太阳的距离排列如下:水星、金星、地球、火星、木星、土星、天王星和海王星。 我们编写一个 Java 方法,它采用行星的序数(相对于它与太阳的距离)并将行星大气的主要成分作为 List<String> 返回。 你会记得有些行星有相似的大气成分。金星和火星主要含有二氧化碳;木星和土星的大气层由氢和氦组成;天王星和海王星的主要成分为甲烷。 下面是功能实现:
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
break;
case 2:
case 4: result.add("Carbon dioxide");
break;
case 3: result.add("Carbon dioxide");
result.add("Nitrogen");
result.add ("Oxygen");
break;
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
break;
case 7:
case 8: result.add("Methane");
result.add("Hydrogen");
result.add("Helium");
break;
default:
break;
}
return result;
}
请注意,我们对大气成分相同的行星使用相同的代码。我们通过使用连续的 case 语句来做到这一点。
如果想得到我们母星的大气成分,我们将 3 作为参数调用方法:
getPlanetAtmosphere(3).
System.out.println(getPlanetAtmosphere(3)) returns ["Carbon dioxide", "Nitrogen", "Oxygen"].
试验 break:
如果删除所有的 break 语句会发生什么?那就试试吧:
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
case 2:
case 4: result.add("Carbon dioxide");
case 3: result.add("Carbon dioxide");
result.add("Nitrogen");
result.add ("Oxygen");
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
case 7:
case 8: result.add("Methane");
result.add("Hydrogen");
result.add("Helium");
default:
}
return result;
}
如果我们输出 System.out.println(getPlanetAtmosphere(3)) 的结果,那么我们会发现我们的母星不是那么宜居。或宜居吗?自行判断:
["二氧化碳", "氮气", "氧气", "氢气", "氦气", "甲烷", "氢气", "氦气"]。
为何发生这种情况?在第一次匹配后,该程序会执行所有的 case 语句,直到 switch 块结束。
过度优化 break 语句
请注意,我们可以通过以不同方式排列 break 语句和 case 来改进该方法。
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
break;
case 3: result.add("Nitrogen");
result.add ("Oxygen");
case 2:
case 4: result.add("Carbon dioxide");
break;
case 7:
case 8: result.add("Methane");
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
}
return result;
}
看起来代码少了,对吧?我们通过调整 case 语句的顺序并重新组合来减少语句的总数。现在,每种类型的气体都添加到列表中,一种气体一行代码。
最后一个例子中给出的代码只是为了展示原理的。我们建议以这种方式编写代码。如果此类 Java 代码的作者(更不用说其他程序员)必须进行维护,会发现很难重构这些 case 块形成背后的逻辑以及在 switch 语句中执行的代码。
与 if 的区别
鉴于 if 和 switch 语句表面很相似,不要忘记 switch 语句根据特定值选择一个 case,而 if 语句可以使用任何布尔表达式。在设计代码时请记住这一点。结论
- 将 case 语句用于两个以上的分支,以免你的代码与 if 语句混淆。
- 不要忘记通过插入 break 语句来完成每个特定值(case 语句)的分支逻辑块。
- switch 语句的表达式可以是 Enum 或 String,以及一些原始类型。
- 记住 default 块。使用它来处理意外的值。
- 要优化性能,请将与最常见值对应的代码分支移动到 switch 块的开头。
- 不要删除 case 语句末尾的 break 语句而执着于“优化” — 这样的代码很难理解,因此也很难维护。
GO TO FULL VERSION