• jdk1.7 String switch的实现


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

    看这样一个程序:

    Java代码  收藏代码
    1. public class Test {     
    2.     public static void main(String[] args) {    
    3.         String name = "b";  
    4.         int value = 0;  
    5.         switch(name) {  
    6.             case "a":  
    7.                 value = 1;  
    8.                 break;  
    9.             case "b":  
    10.                 value = 2;  
    11.                 break;  
    12.             case "c":  
    13.                 value = 3;  
    14.                 break;  
    15.             case "d":  
    16.                 value = 4;  
    17.                 break;  
    18.             case "e":  
    19.                 value = 5;  
    20.                 break;  
    21.             default:  
    22.                 value = 6;  
    23.         }  
    24.         System.out.println(value);  
    25.     }    
    26. }  

    javap -c Test得出的结果为:

    Java代码  收藏代码
    1. public static void main(java.lang.String[]);  
    2.   Code:  
    3.      0: ldc           #2                  // String b  
    4.      2: astore_1                    //将"b"赋值给name  
    5.      3: iconst_0                    //将0入栈  
    6.      4: istore_2                    //将0赋值给value  
    7.      5: aload_1                 //将name(即"b")入栈  
    8.      6: astore_3                    //将name(即"b")赋值给一个编译器生成的变量,记为tmpName(tmpName此时也是"b")  
    9.      7: iconst_m1                   //将-1入栈  
    10.      8: istore        4         //将-1赋值给一个编译器生成的变量,记为m1  
    11.     10: aload_3                 //将tmpName(即"b")入栈  
    12.     11: invokevirtual #3                  // Method java/lang/String.hashCode:()I       调用tmpName的hashCode方法("b".hashCode(),得结果98)  
    13.     14: tableswitch   { // 97 to 101        //根据hashCode的值到不同的分支  
    14.                   97: 48  
    15.                   98: 63                    //这里走到这个分支,跳转到63  
    16.                   99: 78  
    17.                  100: 93  
    18.                  101: 108  
    19.              default: 120  
    20.         }  
    21.     48: aload_3  
    22.     49: ldc           #4                  // String a  
    23.     51: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z  
    24.     54: ifeq          120  
    25.     57: iconst_0  
    26.     58: istore        4  
    27.     60: goto          120  
    28.     63: aload_3                         //从14跳转到了这里,将tmpName(即"b")入栈  
    29.     64: ldc           #2                  // String b       将"b"入栈  
    30.     66: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z     
    31.                                     //比较tmpName和"b",如果相等,将1入栈,不等将0入栈.这里是相等的,入栈的为1  
    32.     69: ifeq          120                   //从栈顶取出比较结果,如果等于0,就跳到120,如果不等于0就继续下面的指令,这里显然不等于0  
    33.     72: iconst_1                            //将1入栈  
    34.     73: istore        4                 //将1存储到m1中  
    35.     75: goto          120                   //跳到120  
    36.     78: aload_3  
    37.     79: ldc           #6                  // String c  
    38.     81: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z  
    39.     84: ifeq          120  
    40.     87: iconst_2  
    41.     88: istore        4  
    42.     90: goto          120  
    43.     93: aload_3  
    44.     94: ldc           #7                  // String d  
    45.     96: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z  
    46.     99: ifeq          120  
    47.    102: iconst_3  
    48.    103: istore        4  
    49.    105: goto          120  
    50.    108: aload_3  
    51.    109: ldc           #8                  // String e  
    52.    111: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z  
    53.    114: ifeq          120  
    54.    117: iconst_4  
    55.    118: istore        4  
    56.    120: iload         4                 //将m1(即73行存进去的1)的值入栈  
    57.    122: tableswitch   { // 0 to 4             
    58.                    0: 156  
    59.                    1: 161                   //这里走1这个分支,跳到161  
    60.                    2: 166  
    61.                    3: 171  
    62.                    4: 176  
    63.              default: 181  
    64.         }  
    65.    156: iconst_1  
    66.    157: istore_2  
    67.    158: goto          184  
    68.    161: iconst_2                    //将2入栈  
    69.    162: istore_2                    //将2存储到value  
    70.    163: goto          184           //跳转到184进行打印输出  
    71.    166: iconst_3  
    72.    167: istore_2  
    73.    168: goto          184  
    74.    171: iconst_4  
    75.    172: istore_2  
    76.    173: goto          184  
    77.    176: iconst_5  
    78.    177: istore_2  
    79.    178: goto          184  
    80.    181: bipush        6  
    81.    183: istore_2  
    82.    184: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;  
    83.    187: iload_2  
    84.    188: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V  
    85.    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

    Java代码  收藏代码
    1. public class Test {     
    2.     public static void main(String[] args) {    
    3.         String name = "buzzards";  
    4.         int value = 0;  
    5.         switch(name) {  
    6.             case "buzzards":  
    7.                 value = 1;  
    8.                 break;  
    9.             case "righto":  
    10.                 value = 2;  
    11.                 break;  
    12.             default:  
    13.                 value = 6;  
    14.         }  
    15.         System.out.println(value);  
    16.     }    
    17. }  

    字节码:

    Java代码  收藏代码
    1. public static void main(java.lang.String[]);  
    2.   Code:  
    3.      0: ldc           #2                  // String buzzards  
    4.      2: astore_1  
    5.      3: iconst_0  
    6.      4: istore_2  
    7.      5: aload_1  
    8.      6: astore_3  
    9.      7: iconst_m1  
    10.      8: istore        4  
    11.     10: aload_3  
    12.     11: invokevirtual #3                  // Method java/lang/String.hashCode:()I  
    13.     14: lookupswitch  { // 1  
    14.           -931102253: 32  
    15.              default: 59  
    16.         }  
    17.     32: aload_3  
    18.     33: ldc           #4                  // String righto  
    19.     35: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)  
    20.     38: ifeq          47  
    21.     41: iconst_1  
    22.     42: istore        4  
    23.     44: goto          59  
    24.     47: aload_3  
    25.     48: ldc           #2                  // String buzzards  
    26.     50: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)  
    27.     53: ifeq          59  
    28.     56: iconst_0  
    29.     57: istore        4  
    30.     59: iload         4  
    31.     61: lookupswitch  { // 2  
    32.                    0: 88  
    33.                    1: 93  
    34.              default: 98  
    35.         }  
    36.     88: iconst_1  
    37.     89: istore_2  
    38.     90: goto          101  
    39.     93: iconst_2  
    40.     94: istore_2  
    41.     95: goto          101  
    42.     98: bipush        6  
    43.    100: istore_2  
    44.    101: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;  
    45.    104: iload_2  
    46.    105: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V  
    47.    108: return  

    这里我们看到两个字符串都跳到32行,首先跟"righto"比较,如果不相等则跳到47行比较buzzards。

  • 相关阅读:
    job 定时任务的五种创建方式
    一步步实现 Redis 搜索引擎
    数据库第一二三范式
    MongoDB数组更新操作$addToSet和$each修饰符
    V8 执行 JavaScript 的过程
    servicebestpractice项目的更新
    公主连结过root检测-frida
    android使用AsyncHttpClient发送请求
    js检测dom元素的变化
    安卓手机关闭防火墙命令
  • 原文地址:https://www.cnblogs.com/zhangyfr/p/8493950.html
Copyright © 2020-2023  润新知