一、引子
不管是什么编程语言,不管你是前缀表达式的语法树机器,还是后缀表达式的栈机器,基本上都是这么三类程序流程控制结构:顺序、循环和分支。而分支结构的两大巨头if和switch,这TM都是老熟人了。以前刚开始学Java的时候,switch的case中不写break,后面的不会再判断case的条件,只要没有break,他就一通执行到底,当时比较稚嫩学语法记现象然后死记硬背,知其然不知其所以然。
只是没写break,但是后面的case条件也不匹配,怎么就刷刷刷往下跑呢?看个例子先。
/**
* 入参:type=b
* 输出结果:b c d e
*/
public void tableswitch(char type) {
switch (type) {
case 'a':
System.out.println('a');
break;
case 'b':
System.out.println('b');
case 'c':
System.out.println('c');
case 'd':
System.out.println('d');
case 'e':
System.out.println('e');
break;
case 'n':
System.out.println('n');
break;
default:
throw new IllegalArgumentException("Unexpected value: " + type);
}
}
/**
* 入参:type=0
* 输出结果:0 100 125
*/
public void lookupswitch(int type) {
switch (type) {
case 0:
System.out.println(0);
case 100:
System.out.println(100);
case 125:
System.out.println(125);
break;
case -1:
System.out.println(-1);
break;
case 1000:
System.out.println(1000);
break;
default:
throw new IllegalArgumentException("Unexpected value: " + type);
}
}
- 在语法层面,switch控制表达式的条件数据类型只能是byte、short、char和int,在JDK7以后,支持Enum和String,当然也支持前面几种基本数据类型的自动拆箱;
- 在JVM字节码层面,switch语句的代码会被编译成tableswitch或lookupswitch字节码;
tableswitch和lookupswitch字节码正是本文要讨论的问题的答案。
二、tableswitch和lookupswitch
当 switch 语句中的 case 分支的条件值比较稀疏时,tableswitch 指令的空间使用率偏低。这种情况下将使用 lookupswitch 指令来替代。lookupswitch 指令的索引表由 int 型的键值(来源于 case 语句块后面的数值)与对应的目标语句偏移量所构成。当 lookupswitch 指令执行时,switch 语句的条件值将和索引表中的 key 进行比较,如果某个 key 和条件值相符,那么将转移到这个 key 对应的分支偏移量继续继续执行,如果没有 key 值符合,执行将在 default分支执行。
Java 虚拟机规定的 lookupswitch 指令的索引表必须根据 key 值排序,这样使用(如采用二分搜索)将会比直接