结合着 class 文件看:
奇怪了,同样的 switch 语言,却对应两个指令:lookupswitch 和 tableswitch。
所以这两个指令肯定是关键突破点。
我们去哪里找这个两个指令的信息呢?
肯定是得找权威资料的:
怎么样?
The Java® Virtual Machine Specification,Java 虚拟机规范,你就大声的告诉我稳不稳?
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-3.html#jvms-3.10
发现这两个指令,在 Compiling Switches 这一小节中是挨在一起的。
找到这里了,你就找到正确答案的门了。我带领大家看一下我通过这个门,看到的门后面的世界。
首先还是给大家带着我自己的理解,翻译一下虚拟机规范里面是怎么介绍这两个指令的:
switch 语句的编译使用的是 tableswitch 和 lookupswitch 这两个指令。
我们先说说 tableswitch 是干啥的。
当 switch 里面的 case 可以用偏移量进行有效表示的时候,我们就用 tableswitch 指令。如果 switch 语句的表达式计算出来的值不在这个偏移量的有效范围内,那么就进入 default 语句。
看不太明白对不对?
没关系,我第一次看的时候也不太明白。别急,我们看看官方示例:
因为我们 case 的条件是 0、1、2 这三个挨在一起的数据,挨在一起就是 near 。所以这个方法就叫做 chooseNear 。
而这个 0、1、2 就是三个连在一起的数字,所以我们可以用偏移量直接找到其对应的下一个需要跳转的地址。
这个就有点类似于数组,直接通过索引下标就能定位到数据。而下标,是一串连续的数字。
这个场景下,我们就可以用 tableswitch。
当 switch 语句里面 case 的值比较“稀疏”(sparse)的时候,用 tableswitch 指令的话空间利用率就会很低下。于是我们就用 lookupswitch 指令来代替 tableswitch。
你注意官网上用的这个词:sparse。
没想到吧,学技术的时候还能学个英语四级单词。
稀疏。翻译过来了,还是读不懂是不是,没有关系。我给你搞个例子:
左边是 java 文件,里面的 case 只有 0、2、4。
右边是字节码文件, tableswitch 里面有0、1、2、3、4。
对应的 class 文件是这样的:
嘿,你说怎么着?莫名其妙多了个 1 和 3 的 case 。你说神奇不神奇?
这是在干嘛?这不就是在填位置嘛。
填位置的目的是什么?不就是为了保证 java 文件里面的 case 对应的值刚好能和偏移量对上吗?
假设这个时候 switch 表达式的值是 2,我直接根据偏移量 2 ,就可以取到 2 对应的接下来需要执行的地方 47,然后接着执行输出语句了: