Bootstrap

java中switch编译结果探秘

版本:jdk11,其他选项默认

1. switch基本语法

switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。

switch(expression){
    case value :
       //语句
       break; //可选
    case value :
       //语句
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
}

switch case 语句有如下规则:

  • switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量
  • switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
  • case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
  • 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
  • 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
  • switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。

2. case自动类型转换

  1. byteshortchar会自动转换成int类型,然后进行比较;
  2. String 类型会调用hashcode()方法,用int类型返回值进行比较,然后使用equals()进行进一步判断
  3. enum类型,会调用对象的ordinal() 方法,返回其对应的int类型下标;

所有的case最终都会被转换成int类型,然后进行编译。

3. case匹配方式 lookupswitch & tableswitch

tableswitch:带有标签的表格。

lookupswitch:带有key索引和标签的表格。

当case比较紧凑时且数值接近0时,通常使用tableswitch,直接使用下标进行索引,例如代码下文代码示例中的chooseNear()方法,可以看到多出了几个选项,指向了default选项L4。效率为O(1)。

当case比较分散时且数值与0距离较远,通常使用lookupswitch,其中的数值进行了排序,采用二分法进行查找,效率为O(log n)。

4. 代码示例

代码:

public class SmallIntDemo {
    public static void main(String[] args) {
        System.out.println(chooseFar(1));
        System.out.println(chooseFar(1));
        System.out.println(charNear('a'));
        System.out.println(stringMethod("hello"));
        System.out.println(enumMethod(CharEnum.a));
    }

    enum CharEnum {
        a,
        b,
        c
    }

    static int chooseNear(int i) {
        switch (i) {
            case 0:
                return 0;
            case -1:
                return 0;
            case 1:
                return 1;
            case 2:
                return 2;
            case 8:
                return 8;
            default:
                return -1;
        }
    }

    static int chooseFar(int i) {
        switch (i) {
            case -100:
                return -1;
            case 0:
                return 0;
            case 100:
                return 1;
            default:
                return -1;
        }
    }

    static int charNear(char c) {
        switch (c) {
            case 'a':
                return 0;
            case 'b':
                return 0;
            case 'c':
                return 1;
            default:
                return -1;
        }
    }

    static int stringMethod(String str) {
        switch (str) {
            case "ok":
                return 0;
            case "fine":
                return 0;
            case "hello":
                return 1;
            default:
                return -1;
        }
    }

    static char enumMethod(CharEnum ce) {
        switch (ce) {
            case a:
                return 'a';
            case b:
                return 'b';
            case c:
                return 'c';
            default:
                return ' ';
        }
    }
}

编译结果:

  // access flags 0x8
  static chooseNear(I)I
   L0
    LINENUMBER 30 L0
    ILOAD 0
    TABLESWITCH
      -1: L1
      0: L2
      1: L3
      2: L4
      3: L5
      4: L5
      5: L5
      6: L5
      7: L5
      8: L6
      default: L5
   L2
    LINENUMBER 32 L2
   FRAME SAME
    ICONST_0
    IRETURN
   L1
    LINENUMBER 34 L1
   FRAME SAME
    ICONST_0
    IRETURN
   L3
    LINENUMBER 36 L3
   FRAME SAME
    ICONST_1
    IRETURN
   L4
    LINENUMBER 38 L4
   FRAME SAME
    ICONST_2
    IRETURN
   L6
    LINENUMBER 40 L6
   FRAME SAME
    BIPUSH 8
    IRETURN
   L5
    LINENUMBER 42 L5
   FRAME SAME
    ICONST_M1
    IRETURN
   L7
    LOCALVARIABLE i I L0 L7 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x8
  static chooseFar(I)I
   L0
    LINENUMBER 47 L0
    ILOAD 0
    LOOKUPSWITCH
      -100: L1
      0: L2
      100: L3
      default: L4
   L1
    LINENUMBER 49 L1
   FRAME SAME
    ICONST_M1
    IRETURN
   L2
    LINENUMBER 51 L2
   FRAME SAME
    ICONST_0
    IRETURN
   L3
    LINENUMBER 53 L3
   FRAME SAME
    ICONST_1
    IRETURN
   L4
    LINENUMBER 55 L4
   FRAME SAME
    ICONST_M1
    IRETURN
   L5
    LOCALVARIABLE i I L0 L5 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x8
  static charNear(C)I
   L0
    LINENUMBER 60 L0
    ILOAD 0
    TABLESWITCH
      97: L1
      98: L2
      99: L3
      default: L4
   L1
    LINENUMBER 62 L1
   FRAME SAME
    ICONST_0
    IRETURN
   L2
    LINENUMBER 64 L2
   FRAME SAME
    ICONST_0
    IRETURN
   L3
    LINENUMBER 66 L3
   FRAME SAME
    ICONST_1
    IRETURN
   L4
    LINENUMBER 68 L4
   FRAME SAME
    ICONST_M1
    IRETURN
   L5
    LOCALVARIABLE c C L0 L5 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x8
  static stringMethod(Ljava/lang/String;)I
   L0
    LINENUMBER 73 L0
    ALOAD 0
    ASTORE 1
    ICONST_M1
    ISTORE 2
    ALOAD 1
    INVOKEVIRTUAL java/lang/String.hashCode ()I
    LOOKUPSWITCH
      3548: L1
      3143098: L2
      99162322: L3
      default: L4
   L1
   FRAME APPEND [java/lang/String I]
    ALOAD 1
    LDC "ok"
    INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
    IFEQ L4
    ICONST_0
    ISTORE 2
    GOTO L4
   L2
   FRAME SAME
    ALOAD 1
    LDC "fine"
    INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
    IFEQ L4
    ICONST_1
    ISTORE 2
    GOTO L4
   L3
   FRAME SAME
    ALOAD 1
    LDC "hello"
    INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z
    IFEQ L4
    ICONST_2
    ISTORE 2
   L4
   FRAME SAME
    ILOAD 2
    TABLESWITCH
      0: L5
      1: L6
      2: L7
      default: L8
   L5
    LINENUMBER 75 L5
   FRAME SAME
    ICONST_0
    IRETURN
   L6
    LINENUMBER 77 L6
   FRAME SAME
    ICONST_1
    IRETURN
   L7
    LINENUMBER 79 L7
   FRAME SAME
    ICONST_2
    IRETURN
   L8
    LINENUMBER 81 L8
   FRAME SAME
    ICONST_M1
    IRETURN
   L9
    LOCALVARIABLE str Ljava/lang/String; L0 L9 0
    MAXSTACK = 2
    MAXLOCALS = 3

  // access flags 0x8
  static enumMethod(Lswitch_case/SmallIntDemo$CharEnum;)C
   L0
    LINENUMBER 86 L0
    GETSTATIC switch_case/SmallIntDemo$1.$SwitchMap$switch_case$SmallIntDemo$CharEnum : [I
    ALOAD 0
    INVOKEVIRTUAL switch_case/SmallIntDemo$CharEnum.ordinal ()I
    IALOAD
    TABLESWITCH
      1: L1
      2: L2
      3: L3
      default: L4
   L1
    LINENUMBER 88 L1
   FRAME SAME
    BIPUSH 97
    IRETURN
   L2
    LINENUMBER 90 L2
   FRAME SAME
    BIPUSH 98
    IRETURN
   L3
    LINENUMBER 92 L3
   FRAME SAME
    BIPUSH 99
    IRETURN
   L4
    LINENUMBER 94 L4
   FRAME SAME
    BIPUSH 32
    IRETURN
   L5
    LOCALVARIABLE ce Lswitch_case/SmallIntDemo$CharEnum; L0 L5 0
    MAXSTACK = 2
    MAXLOCALS = 1
}

4. 参考文档

[1] Java switch case 语句

[2] Chapter 3. Compiling for the Java Virtual Machine

[3] stackoverflow: Difference between JVM’s LookupSwitch and TableSwitch?

;