switch 语句是一种有多路分支语句。
对于 Java 而言,通常会涉及以下知识点:
- switch 可使用的数据类型是受限的;
- switch 存在贯穿(fallthrough)现象;
受限的数据类型
switch 语句中可使用的数据类型有:
- 基本数据类型 byte, short, char, int 及其封装类型
- 字符串
- 枚举类型
贯穿
如果 case 分支中的代码不以 break 结束,那么就会继续执行下一个 case 分支,这种行为称为“贯穿(fallthrough)”。
@Test
public void testFallThrough() {
int i = 3;
switch (i) {
case 2:
System.out.println("Two");
break;
case 3:
System.out.println("Three");
case 4:
System.out.println("Four");
}
}
如上面的测试方法被执行时会输出:
Three
Four
这不是上面程序的正确逻辑,而仅仅是我们忘记加 break 语句了。但 Java 编译器默认不对此进行检查,要想开启 fallthrough 检查需要在编译时添加参数 -Xlint:fallthrough
。
javac -Xlint:fallthrough XXX.java
加上参数再次编译上文程序就会得到如下警告:
Switch.java:11: 警告: [fallthrough] 可能无法实现 case
case 4:
^
但有时我们的确会使用贯穿作为一种编程技巧,这时,我们并不希望因为开启检查而出现不必要的警告。这种情况下,可以使用@SuppressWarnings("fallthrough")
抑制检查警告。当然,同时还应该注释说明此处是有意利用贯穿,而不是 Bug。
局部变量作用域
另外,有个生僻的知识点,是关于 case 分支中的局部变量作用域的。
case 分支中声明的局部变量在所有分支中使用前都必需初始化。
通常,case 分支中不论有多少条语句都不需要使用花括号括起来,当然也可以括起来。
显然,有没有花括号会直接影响作用域。没有花括号时,代码处于 switch 块作用域中;有花括号则处于花括号的作用域中。
一般而言,几乎没有什么差别。但是,来看下下面这段代码:
@Test
public void testVarInBlock() {
int i = 3;
switch (i) {
case 2:
int j = 2;
System.out.println(i + j);
break;
case 3:
j = 3;
System.out.println(i + j);
break;
case 4:
j = 4;
System.out.println(i + j);
break;
}
}
注意 case 2 分支中声明了局部变量 j
,它的作用域为 switch 块,因此,可以在 case 3 和 case 4 分支中使用它——显然在 case 3 和 case 4 中我们不能重复声明 j
。
但是,有一点可能让人意外的是,每个 case 分支都必需要初始化变量 j
。即如果没有 j = 3;
或 j = 4;
则会编译错误,提示变量未初始化。
不过,这点在逻辑上还是很好想通的,如果不是每个 case 都执行初始化操作,那么在执行某个未执行初始化操作的 case 时,使用的就是未初始化的局部变量。
其他未讨论的
- default 分支可有可无。
- default 分支的位置是任意的,但是“意外”贯穿的结果会不同。
- case 关键字后跟的是常量表达式
- 不能是
null
,因为null
不是常量 - 注意是常量“表达式”,因此可能是:
42
,"字符串字面量"
,CONST_VALUE
,42 * 2
…
- 不能是
- 还有没想到的, 想到再加……
【完】