zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    自定义类型
    基本类型
    个人的职业规划
    FastDFS .Net客户端使用指南
    Delphi中资源的简单应用
    GridView数据绑定
    GridView的分页功能
    硬盘最多能分几个区?
    C#中public、private、protected、internal、protected internal
    2007.10.09错误记录
  • 原文地址:https://www.cnblogs.com/sky7034/p/2145489.html
Copyright © 2011-2022 走看看