Java:通过字节码看if-else和switch-case

Posted bdmh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:通过字节码看if-else和switch-case相关的知识,希望对你有一定的参考价值。

欢迎交流

条件语句是我们使用非常频繁的语法之一,其中if-elseswitch-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)去和 做比较,如果不相等,跳转至第30行,取出第三个数值(3)去和 做比较,如果不相等,跳转到第42行,结束。

从字节码看,确实if-else是顺序判断,直到找到符合条件的。

switch-case又是怎样工作的呢?

先大概讲一下,switch-case会创建一个table,每条case的值,作为key,对应着这个条件要跳转到的指令行。

table有两种,tableswitchlookupswitch,每个条件的值之间的比较连贯的,会使用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,如果在,那就定位就好了,所以需要是一个连续的tabletable中每一个数值后面对应这要跳转到的指令行,根据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:从字节码看Enum类型

Java:从字节码看Enum类型

Java:从字节码看Enum类型

从字节码看Java中for-each循环(增强for循环)实现原理

有啥完美的方法替代java中的 if-else,switch-case

if-else 和 switch 语句的替代方案