说说Java中你不知道switch关键字
Switch语法
switch
作为Java内置关键字,却在项目中真正使用的比较少。关于switch
,还是有那么一些奥秘的。
要什么switch,我有if-else
确实,项目中使用switch
比较少的一个主要原因就在于它的作用能被if-else
代替,况且switch
对类型的限制,也阻碍了switch
的进一步使用。
先看看switch
的语法:
switch(exp){
case exp1:
break;
case exp2:
break;
default:
break;
}
其中exp
的类型限制为:byte ,short , int , char,
及其包装类,以及枚举和String
(JDK1.7)
为什么要有这些限制?
如果说,switch
的功能和if-else
的一模一样,那么它存在的意义在哪里?
答案是:switch
和if-else
在设计的时候,是有一定的性能差别的。
看代码:
public class Test {
public static void switchTest(int a) {
switch (a) {
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
default:
System.out.println("3");
break;
}
}
}
javap -c Test.class
结果如下:
public static void switchTest(int);
Code:
0: iload_0
1: lookupswitch { // 2
1: 28
2: 39
default: 50
}
...
这里面省略一些代码。
可以发现,switch
是通过lookupswitch
指令实现。那么lookupswitch
指令是干嘛的呢?
在Java se8文档中的描述可以大概知道:
switch
可以被编译为两种指令
-
lookupswitch
:当switch
的case
比较稀疏的时候,使用该指令对int
值的case
进行一一比较,直至找到对应的case
(这里的查找,可以优化为二分查找) -
tableswitch
:当switch
的case
比较密集的时候,使用case
的值作为switch
的下标,可以在时间复杂度为O(1)的情况下找到对应的case
(可以类比HashMap)
并且文档中还有一段描述:
Java虚拟机的
tableswitch
和lookupswitch
指令仅对int
数据有效。因为对byte
,char
或或short
值的操作在内部被提升为int
,所以对其switch
表达式求值为其中一个类型进行编译,就好像它被计算为要键入一样int
。如果chooseNear
方法是使用type编写的,则使用类型时short
将生成相同的Java虚拟机指令int
。其他数字类型必须缩小到类型int
以便在a中使用switch
。
现在,我们应该能够明白,为什么switch
关键字会有类型限制了,因为 switch
所被翻译的关键字是被限制为int类型的,至于为什么是int,我猜应该是基于性能和实现的复杂度的考量吧。
int之外的类型
我们明白了byte,shor,char,int
能被作为switch
类型后,再看看枚举和String
public static void switchTest(String a) {
switch (a) {
case "1":
System.out.println("1");
break;
case "2":
System.out.println("2");
break;
default:
System.out.println("3");
break;
}
}
编译生成Test.class。拖入IDEA进行反编译得到如下代码:
public static void switchTest(String a) {
byte var2 = -1;
switch(a.hashCode()) {
case 49:
if (a.equals("1")) {
var2 = 0;
}
break;
case 50:
if (a.equals("2")) {
var2 = 1;
}
}
switch(var2) {
case 0:
System.out.println("1");
break;
case 1:
System.out.println("2");
break;
default:
System.out.println("3");
}
}
可以看见,JDK7 所支持的String
类型是通过获取String
的hashCode来进行选择的,也就是本质上还是int.为什么String
可以这样干?这取决于String
是一个不变类。
为了防止hash碰撞,代码更加保险的进行了
equals
判断。
再来看看Enum
public static void switchTest(Fruit a) {
switch (a) {
case Orange:
System.out.println("Orange");
break;
case Apple:
System.out.println("Apple");
break;
default:
System.out.println("Banana");
break;
}
}
编译生成Test.class。拖入IDEA进行反编译得到如下代码:
public static void switchTest(Fruit a) {
switch(1.$SwitchMap$com$dengchengchao$Fruit[a.ordinal()]) {
case 1:
System.out.println("Orange");
break;
case 2:
System.out.println("Apple");
break;
default:
System.out.println("Banana");
}
}
可以看到,枚举支持switch
更加简单,直接通过枚举的顺序即可作为相关case
总之:
-
switch
的设计按道理来说,是比if-else
要快的,但是在99.99%的情况下,他们性能差不多,除非case
分支量巨大,但是在case
分支过多的情况下,一般应该考虑使用多态重构了。 -
switch
虽然支持byte,int,short,char,enum,String
但是本质上都是int
,其他的只是编译器帮你进行了语法糖优化而已。
尊重劳动成果,转载注明出处
如果觉得写得不错,欢迎关注微信公众号:逸游Java ,每天不定时发布一些有关Java进阶的文章,感谢关注