Java:通过字节码看if-else和switch-case
Posted bdmh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:通过字节码看if-else和switch-case相关的知识,希望对你有一定的参考价值。
条件语句是我们使用非常频繁的语法之一,其中if-else和switch-case最为常用,那很多同学也会有疑惑,这两种方式有什么区别,哪个效率更高一些呢。接下来我们就从字节码的角度,看看这两种方法的实现。
先来看看if-else。
public void foo(int i)
if (i == 1)
System.out.println(1);
else if (i == 2)
System.out.println(2);
else if (i == 3)
System.out.println(3);
从代码字面上理解,应该是自上而下,以此判断,那到底是不是呢,看看字节码。
iload_1将变量(i)推至栈顶,iconst_1将第一个整型常量(1)推至栈顶。
然后通过if_icmpne来判断这两个值是否相等,如果相等,就执行打印,然后跳转到第42行,结束(下同),如果不相等,就跳转至第15行。15行这次是取出第二个常量(2)去和 i 做比较,如果不相等,跳转至第30行,取出第三个数值(3)去和 i 做比较,如果不相等,跳转到第42行,结束。
从字节码看,确实if-else是顺序判断,直到找到符合条件的。
那switch-case又是怎样工作的呢?
先大概讲一下,switch-case会创建一个table,每条case的值,作为key,对应着这个条件要跳转到的指令行。
table有两种,tableswitch和lookupswitch,每个条件的值之间的比较连贯的,会使用tableswitch,否则会使用lookupswitch,而且lookupswitch会对所有的case的值进行排序,保证是有序的,这样可以提高查找效率。
接下来看例子。
public void useCase(int i)
switch (i)
case 1:
System.out.println(1);
break;
case 2:
System.out.println(2);
break;
case 4:
System.out.println(4);
break;
case 6:
System.out.println(6);
break;
default:
System.out.println(0);
1,2,4,6这是四种条件分支,不连续,但是间隔较小,所以JVM会使用tableswitch,看看字节码是什么样。
public void useCase(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
//注意table中的数据
1: tableswitch // 1 to 6
1: 40
2: 50
3: 80
4: 60
5: 80
6: 70
default: 80
先看看tableswitch的内容,我们发现,比我们的四种数据多了两种,把1~6之间的缺少的值,补充了进去,形成一个连续的表。它们对应的指令行都是80,那第80行是什么呢?其实就是default语句那一个分支。因为补充的这两个在代码中是不存在的,所以就让它指向default就行了。
当判断时,系统会判断你输入的值是否在1~6之间,如果不在,就直接跳到default,如果在,那就定位就好了,所以需要是一个连续的table。table中每一个数值后面对应这要跳转到的指令行,根据key取到行号,跳就是了。
40,50,60,70分别对应这每个条件的打印代码,执行完后,跳转到87(return)。
下面我们把条件的值改一下,让它的间隔大一些。
public void useCase(int i)
switch (i)
case 10:
System.out.println(10);
break;
case 50:
System.out.println(50);
break;
case 30:
System.out.println(30);
break;
default:
System.out.println(0);
我们的顺序是10,50,30,看看字节码。
public void useCase(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: lookupswitch // 3
10: 36
30: 58
50: 47
default: 69
系统使用了lookupswitch,而且,它的排序是10,30,50,是按照升序排列的,变成了有序的。也没有像tableswitch那样,填充间隔,所以它们的查找方式是不一样的。
因为是有序的,所以可以采取诸如二分法的算法,来提高查找效率。
理解力两种条件语句的实现方式,那么该如何选择呢?
如果条件简单而且比较少if-else也没问题,毕竟switch-case要生成一个table,占用了一定的空间,如果条件多,if-else不但不美观,而且顺序执行的效率也是比较低的。所以这时候采用switch-case是合理的。
以上是关于Java:通过字节码看if-else和switch-case的主要内容,如果未能解决你的问题,请参考以下文章
从字节码看Java中for-each循环(增强for循环)实现原理