switch 作为Java内置关键字,却在项目中真正使用的比较少。关于 switch ,还是有那么一些 奥秘 的。
确实,项目中使用 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,我猜应该是基于性能和实现的复杂度的考量吧。
我们明白了 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进阶的文章,感谢关注