jdk1.7 String switch的实现

对于int的switch,jvm是用tableswitch和lookupswitch来实现的,jdk1.7 switch增加了对string的支持,那么底层是如何实现的呢?是否增加了新的指令或是否给某些指令增加了新的含义?

看这样一个程序:

public class Test {  
    public static void main(String[] args) { 
        String name = "b";
        int value = 0;
        switch(name) {
            case "a":
                value = 1;
                break;
            case "b":
                value = 2;
                break;
            case "c":
                value = 3;
                break;
            case "d":
                value = 4;
                break;
            case "e":
                value = 5;
                break;
            default:
                value = 6;
        }
        System.out.println(value);
    } 
}

javap -c Test得出的结果为:

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String b
       2: astore_1                    //将"b"赋值给name
       3: iconst_0                    //将0入栈
       4: istore_2                    //将0赋值给value
       5: aload_1                    //将name(即"b")入栈
       6: astore_3                    //将name(即"b")赋值给一个编译器生成的变量,记为tmpName(tmpName此时也是"b")
       7: iconst_m1                    //将-1入栈
       8: istore        4            //将-1赋值给一个编译器生成的变量,记为m1
      10: aload_3                    //将tmpName(即"b")入栈
      11: invokevirtual #3                  // Method java/lang/String.hashCode:()I        调用tmpName的hashCode方法("b".hashCode(),得结果98)
      14: tableswitch   { // 97 to 101        //根据hashCode的值到不同的分支
                    97: 48
                    98: 63                    //这里走到这个分支,跳转到63
                    99: 78
                   100: 93
                   101: 108
               default: 120
          }
      48: aload_3
      49: ldc           #4                  // String a
      51: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      54: ifeq          120
      57: iconst_0
      58: istore        4
      60: goto          120
      63: aload_3                            //从14跳转到了这里,将tmpName(即"b")入栈
      64: ldc           #2                  // String b        将"b"入栈
      66: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z   
                                            //比较tmpName和"b",如果相等,将1入栈,不等将0入栈.这里是相等的,入栈的为1
      69: ifeq          120                    //从栈顶取出比较结果,如果等于0,就跳到120,如果不等于0就继续下面的指令,这里显然不等于0
      72: iconst_1                            //将1入栈
      73: istore        4                    //将1存储到m1中
      75: goto          120                    //跳到120
      78: aload_3
      79: ldc           #6                  // String c
      81: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      84: ifeq          120
      87: iconst_2
      88: istore        4
      90: goto          120
      93: aload_3
      94: ldc           #7                  // String d
      96: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      99: ifeq          120
     102: iconst_3
     103: istore        4
     105: goto          120
     108: aload_3
     109: ldc           #8                  // String e
     111: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
     114: ifeq          120
     117: iconst_4
     118: istore        4
     120: iload         4                    //将m1(即73行存进去的1)的值入栈
     122: tableswitch   { // 0 to 4           
                     0: 156
                     1: 161                    //这里走1这个分支,跳到161
                     2: 166
                     3: 171
                     4: 176
               default: 181
          }
     156: iconst_1
     157: istore_2
     158: goto          184
     161: iconst_2                    //将2入栈
     162: istore_2                    //将2存储到value
     163: goto          184            //跳转到184进行打印输出
     166: iconst_3
     167: istore_2
     168: goto          184
     171: iconst_4
     172: istore_2
     173: goto          184
     176: iconst_5
     177: istore_2
     178: goto          184
     181: bipush        6
     183: istore_2
     184: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     187: iload_2
     188: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V
     191: return
}

在这一堆指令中我们发现,jdk1.7并没有新指令来处理string switch,还是继续用lookupswitch和tableswitch两个指令来处理的,也没有扩展这两个指令,它们还是只能处理int。

在11行,我们看到调用了需switch的string的hashCode方法,并对该hashCode进行switch,并跳转到相应的处理指 令。跳到新的指令处我们发现这里(63行)将待switch的变量(name)与case中的值("b")equals了一下。这样做是为了避免不同的 string有相同的hashCode,确定equals返回true后,编译器生成了一个处理value的switch,源码里有多少个case编译器 生成的tableswitch就有多少个分支,最终会找到相应的处理分支完成string switch的处理。

接下来,看一个不同string hashCode相等的版本:

buzzards与righto的hashCode相等

hierarch与crinolines的hashCode相等

这里选择buzzards和righto

public class Test {  
    public static void main(String[] args) { 
        String name = "buzzards";
        int value = 0;
        switch(name) {
            case "buzzards":
                value = 1;
                break;
            case "righto":
                value = 2;
                break;
            default:
                value = 6;
        }
        System.out.println(value);
    } 
}

字节码:

 public static void main(java.lang.String[]);
   Code:
      0: ldc           #2                  // String buzzards欠款
      2: astore_1
      3: iconst_0
      4: istore_2
      5: aload_1
      6: astore_3
      7: iconst_m1
      8: istore        4
     10: aload_3
     11: invokevirtual #3                  // Method java/lang/String.hashCode:()I
     14: lookupswitch  { // 1
           -931102253: 32
              default: 59
         }
     32: aload_3
     33: ldc           #4                  // String righto
     35: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)
     38: ifeq          47
     41: iconst_1
     42: istore        4
     44: goto          59
     47: aload_3
     48: ldc           #2                  // String buzzards
     50: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)
     53: ifeq          59
     56: iconst_0
     57: istore        4
     59: iload         4
     61: lookupswitch  { // 2
                    0: 88
                    1: 93
              default: 98
         }
     88: iconst_1
     89: istore_2女装品牌大全
     90: goto          101
     93: iconst_2
     94: istore_2
     95: goto          101
     98: bipush        6
    100: istore_2
    101: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
    104: iload_2
    105: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
    108: return

原文地址:https://www.cnblogs.com/sky7034/p/2145489.html