编译器为什么不会在每个代码块结束后自动添加break语句?这是出于历史原因吗?何时需要执行多个代码块?
编译器为什么不会在每个代码块结束后自动添加break语句?这是出于历史原因吗?何时需要执行多个代码块?
有时候将多个情况与相同的代码块关联起来是很有帮助的,比如
case 'A':
case 'B':
case 'C':
doSomething();
break;
case 'D':
case 'E':
doSomethingElse();
break;
等等,只是一个例子。
在我的经验中,通常情况下,在一个case下执行多个代码块不是好的编程风格,但在某些情况下可能会有用处。
case
被堆叠在一起,我就不会费心写注释。如果它们之间有代码,则可能需要注释。 - Billy ONealcase
语句中声明多个情况,像这样: case 'A','B','C':doSomething(); case 'D','E':doSomethingElse();
,不需要在情况之间加上break
。Pascal可以做到这一点:“case
语句将枚举表达式的值与每个选择器进行比较,选择器可以是常量、子范围或由逗号分隔的列表。”(http://wiki.freepascal.org/Case) - Christian SemrauJava的语法来源于C语言。
有时候你希望多个case语句只有一个执行路径。下面是一个例子,可以告诉你一个月有多少天。
class SwitchDemo2 {
public static void main(String[] args) {
int month = 2;
int year = 2000;
int numDays = 0;
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
numDays = 31;
break;
case 4:
case 6:
case 9:
case 11:
numDays = 30;
break;
case 2:
if ( ((year % 4 == 0) && !(year % 100 == 0))
|| (year % 400 == 0) )
numDays = 29;
else
numDays = 28;
break;
default:
System.out.println("Invalid month.");
break;
}
System.out.println("Number of Days = " + numDays);
}
}
你可以通过case穿透实现各种有趣的事情。
例如,假设你想对所有情况执行特定操作,但在某个情况下,你还想执行该操作和其他一些操作。使用带有case穿透的switch语句就可以轻松实现。
switch (someValue)
{
case extendedActionValue:
// do extended action here, falls through to normal action
case normalActionValue:
case otherNormalActionValue:
// do normal action here
break;
}
当然,容易忘记在case语句的结尾添加break
语句,导致意外行为。优秀的编译器会在省略break语句时发出警告。
public class SwitchExpressionsNoFallThrough {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int value = scanner.nextInt();
/*
* Before JEP-325
*/
switch (value) {
case 1:
System.out.println("one");
case 2:
System.out.println("two");
default:
System.out.println("many");
}
/*
* After JEP-325
*/
switch (value) {
case 1 ->System.out.println("one");
case 2 ->System.out.println("two");
default ->System.out.println("many");
}
}
}
在 使用JDK-12执行上述代码时,可以看到比较输出
//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one
并且
//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two
当然,未改变的事物
// input
3
many // default case match
many // branches to 'default' as well
case THIS:
case THAT:
{
code;
break;
}
或者你可以做这样的事情:
case THIS:
{
do this;
}
case THAT:
{
do that;
}
以级联的方式。
如果你问我,这确实容易出现错误和混淆。
do this
和 do that
吗?还是只有在这种情况下才会运行 do that
? - JonnyRaabreak
会允许“多个代码块执行”,更关注的是这种设计选择的动机。其他人提到了从 C 到 Java 的众所周知的遗产,而这个答案甚至将研究推到了 C 语言之前的年代。我希望我们从一开始就有这种(虽然非常原始)的模式匹配。 - wlnirvana正如之前所说的一样,这是为了允许贯穿,不是错误,而是一种特性。
如果太多的break
语句困扰您,您可以通过使用return
语句来轻松摆脱它们。实际上,这是一个好的实践,因为您的方法应该尽可能地小(为了可读性和可维护性),因此,一个switch
语句对于一个方法来说已经足够大了,因此,一个好的方法不应包含任何其他内容,这是一个例子:
public class SwitchTester{
private static final Log log = LogFactory.getLog(SwitchTester.class);
public static void main(String[] args){
log.info(monthsOfTheSeason(Season.WINTER));
log.info(monthsOfTheSeason(Season.SPRING));
log.info(monthsOfTheSeason(Season.SUMMER));
log.info(monthsOfTheSeason(Season.AUTUMN));
}
enum Season{WINTER, SPRING, SUMMER, AUTUMN};
static String monthsOfTheSeason(Season season){
switch(season){
case WINTER:
return "Dec, Jan, Feb";
case SPRING:
return "Mar, Apr, May";
case SUMMER:
return "Jun, Jul, Aug";
case AUTUMN:
return "Sep, Oct, Nov";
default:
//actually a NullPointerException will be thrown before reaching this
throw new IllegalArgumentException("Season must not be null");
}
}
}
12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb
12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May
12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug
12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov
正如预期的。
Java源自C语言,其继承了一种称为Duff's Device的技术。这是一种优化技术,它依赖于在没有break;
语句的情况下从一个case转到下一个case。在C语言标准化之前,已经有很多这样的代码存在,如果改变语言以打破这些构造,将会适得其反。
break
。 - Naman