【Java解惑】 switch 语句的编译实现细节

1,150 阅读2分钟

Java switch语句

问题起源:一次使用 switch 没有使用 break 引起了生产上 bug 后(switch-break 还是需要注意注意在注意),后来想着看看 switch 编译后到底为啥需要 break,单纯的想看看 switch 语句编译后是什么样子。随发现了奇怪的现象,也就是 switch 关于对 String 类型的支持的实现过程。

通过示例分析下 switch 对于 int char 和 String 的支持,其中 String 为 jdk1.7 新特性

  • switch 关于 int 的编译

     private void switchInt(Integer i) {
        switch (i){
        case 0:
            System.out.println("0");
            break;
        case 1:
            System.out.println("1");
            break;
        case 2:
            System.out.println("2");
            break;
        default:
        }
    }
    

    编译后结果:

        private void switchInt(Integer var1) {
        switch(var1) {
        case 0:
            System.out.println("0");
            break;
        case 1:
            System.out.println("1");
            break;
        case 2:
            System.out.println("2");
        }
    
    }
    
    

    总结:int 类型的 switch 语句编译前后没有任何区别

  • switch 关于 char 的编译

    private void switchChar(char c) {
        switch (c){
        case 0:
            System.out.println("0");
            break;
        case 1:
            System.out.println("1");
            break;
        case 2:
            System.out.println("2");
            break;
        default:
        }
    }
    

    编译后结果:

        private void switchChar(char var1) {
        switch(var1) {
        case '\u0000':
            System.out.println("0");
            break;
        case '\u0001':
            System.out.println("1");
            break;
        case '\u0002':
            System.out.println("2");
        }
    
    }
    

    总结:唯一的编译操作就是将非 char 字符动态转换成 char 字符

  • switch 关于 String 的编译

     private void switchString(String s) {
        switch (s){
        case "0":
            System.out.println("0");
            break;
        case "1":
            System.out.println("1");
            break;
        case "2":
            System.out.println("2");
            break;
        default:
        }
    }
    

    编译后结果:

        private void switchString(String var1) {
        byte var3 = -1;
        switch(var1.hashCode()) {
        case 48:
            if (var1.equals("0")) {
                var3 = 0;
            }
            break;
        case 49:
            if (var1.equals("1")) {
                var3 = 1;
            }
            break;
        case 50:
            if (var1.equals("2")) {
                var3 = 2;
            }
        }
    
        switch(var3) {
        case 0:
            System.out.println("0");
            break;
        case 1:
            System.out.println("1");
            break;
        case 2:
            System.out.println("2");
        }
    
    }
    

    总结:这里就是重点了,jdk7 对于 String 的支持原则上还是基于原jdk对于 int 的支持,将原 string 的比较转换成 hashcode,也就是相当于 int 的类型比较,再将字符串比较放在 case 语句中使用 equals 比较,使用新定义的 int 接收,再使用原生的 switch 语句实现具体操作。这算是转圜这实现了对于 string 的支持了。

    引发的新的小问题:char 字符编译前后的区别

    示例代码:

    private void switchChar(char c) {
    
        switch (c){
        case 0:
            System.out.println("0");
            break;
        case '0':
            System.out.println("12");
            break;
        case ' ':
            System.out.println("123");
            break;
        case 1:
            System.out.println("1");
            break;
        case 2:
            System.out.println("2");
            break;
        default:
        }
    }
    

    编译后:

      private void switchChar(char var1) {
        switch(var1) {
        case '\u0000':
            System.out.println("0");
            break;
        case '\u0001':
            System.out.println("1");
            break;
        case '\u0002':
            System.out.println("2");
            break;
        case ' ':
            System.out.println("123");
            break;
        case '0':
            System.out.println("12");
        }
    }
    

    总结:发现一个奇怪的问题,就是关于 char 的编译,将数字 0 转换成 '\u0000',可是百度一番确看到的都是 空格会被转换成 '\u0000',有点不是很理解,单独拿出来备注下。

    // jdk 中关于 Character 的源码中的 '\u0000'
    
        /**
     * The constant value of this field is the smallest value of type
     * {@code char}, {@code '\u005Cu0000'}.
     *
     * @since   1.0.2
     */
    public static final char MIN_VALUE = '\u0000';
    
    /**
     * The constant value of this field is the largest value of type
     * {@code char}, {@code '\u005CuFFFF'}.
     *
     * @since   1.0.2
     */
    public static final char MAX_VALUE = '\uFFFF';
    
    

    关于'\u0000'问题依然没有解决。

    ` 同时回忆下原生的Java编译,不可忘本。

    javac TestDemo.java `