谁是代码界3%的王者?- 第三题switch问题简单解读

简介: 谁是代码界3%的王者?- 第三题switch问题简单解读

 一、背景

 

阿里技术的公众发了一篇文章《谁是代码界3%的王者?》

提到“在Java代码界,有些陷阱外表看起来是个青铜实际上是王者,据说97%工程师会被“秒杀””

给出了五道题,非常考验基础。

本文简单解读第3题,并分享通用的学习和研究方法。

二、题目

这段代码输出的结果是:

A: null

B: 抛出异常

C: default

public static void main(String[] args) {
        String param = null;
        switch (param) {
            case "null":
                System.out.println("null");
                break;
            default:
                System.out.println("default");
        }
    }
}

image.gif

我想大多人在B和C犹豫不决。

因为我们学switch的时候没专门有例子给出这种例子传入null,且学switch的时候default表示不满足其他case的时候会执行,因此猜测很可能打印default。

不过因为是null,会不会发生空指针呢?

我们运行就可以看到结果(空指针异常),但是我们下面从其他更权威的方法进行分析。

三、上法宝

3.1 源码大法

和第五题的解析不同的是switch无法进入到其JDK”源码“中,暂时放弃框架或JDK源码大法

3.2 官方文档大法

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.11

switch的表达式必须是char, byte, short, int, Character, Byte, Short, Integer, String, or an enum类型, 否则会发生编译错误

switch语句必须满足以下条件,否则会出现编译错误:

    • 与switch语句关联的每个case都必须和switch的表达式的类型一致。
    • 如果 switch表达式是枚举类型,  case 常量也必须是枚举类型.
    • 不允许同一个switch的两个case常量的值相同.
    • 和switch语句关联的常量不能为null.
    • 一个switch语句最多有一个default标签.

    When the switch statement is executed, first the Expression is evaluated. If the Expression evaluates to null, a NullPointerException is thrown and the entire switch statement completes abruptly for that reason.

    switch语句执行的时候, 首先将执行switch的表达式.如果表达式为 null, 则会抛出NullPointerException,整个switch语句的执行将被中断.

    答案就显而易见了,B抛出异常,且为空指针异常。

    3.3 java反解析大法

    我们先看一个正常的例子

    public static void main(String[] args) {
            String param = "t";
            switch (param) {
                case "a":
                    System.out.println("a");
                    break;
                case "b":
                    System.out.println("b");
                    break;
                case "c":
                    System.out.println("c");
                    break;
                default:
                    System.out.println("default");
            }
    }

    image.gif

    javap -c SwitchTest

    对应的反汇编代码(稳住!!看不懂不要方,后面有个简化版)

    Compiled from "SwitchTest.java"
    public class com.chujianyun.common.style.SwitchTest {
      public com.chujianyun.common.style.SwitchTest();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
      public static void main(java.lang.String[]);
        Code:
           0: ldc           #2                  // String t
           2: astore_1
           3: aload_1
           4: astore_2
           5: iconst_m1
           6: istore_3
           7: aload_2
           8: invokevirtual #3                  // Method java/lang/String.hashCode:()I
          11: tableswitch   { // 97 to 99
                        97: 36
                        98: 50
                        99: 64
                   default: 75
              }
          36: aload_2
          37: ldc           #4                  // String a
          39: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
          42: ifeq          75
          45: iconst_0
          46: istore_3
          47: goto          75
          50: aload_2
          51: ldc           #6                  // String b
          53: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
          56: ifeq          75
          59: iconst_1
          60: istore_3
          61: goto          75
          64: aload_2
          65: ldc           #7                  // String c
          67: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
          70: ifeq          75
          73: iconst_2
          74: istore_3
          75: iload_3
          76: tableswitch   { // 0 to 2
                         0: 104
                         1: 115
                         2: 126
                   default: 137
              }
         104: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
         107: ldc           #4                  // String a
         109: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         112: goto          145
         115: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
         118: ldc           #6                  // String b
         120: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         123: goto          145
         126: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
         129: ldc           #7                  // String c
         131: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         134: goto          145
         137: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
         140: ldc           #10                 // String default
         142: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         145: return
    }

    image.gif

    关键点

    第8行:调用t的hashCode获取其哈希值。

    第11行:计算swich的case的哈希值a 为97,  b为98,c为99

    依次执行到36行,50行和64行,default为75行。

    依次判断a.equals(param)是否为true,如果是则跳转到打印的语句,然后再跳转到145行退出;否则跳转到default语句打印并退出。

    在编译题目的源码:

    Compiled from "SwitchTest.java"
    public class com.chujianyun.common.style.SwitchTest {
      public com.chujianyun.common.style.SwitchTest();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
      public static void main(java.lang.String[]);
        Code:
           0: aconst_null
           1: astore_1
           2: aload_1
           3: astore_2
           4: iconst_m1
           5: istore_3
           6: aload_2
           7: invokevirtual #2                  // Method java/lang/String.hashCode:()I
          10: lookupswitch  { // 1
                   3392903: 28
                   default: 39
              }
          28: aload_2
          29: ldc           #3                  // String null
          31: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
          34: ifeq          39
          37: iconst_0
          38: istore_3
          39: iload_3
          40: lookupswitch  { // 1
                         0: 60
                   default: 71
              }
          60: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          63: ldc           #3                  // String null
          65: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          68: goto          79
          71: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          74: ldc           #7                  // String default
          76: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          79: return
    }

    image.gif

    根据第7行可知先调用了param.hashCode()函数,然后将参数的hashCode和case比,

    如果上面看不懂没关系,重点看这里:

    switch语句表达式大致等价于

    String param = null;
    int hashCode = param.hashCode();
    if(hashCode==("null").hashCode()&&param.equals("null")){     
       System.out.println("null");
    }else{
       System.out.println("default");
    }

    image.gif

    显然param.hashCode()这里会空指针。

    另外我们打印

    System.out.println(("null").hashCode());

    image.gif

    发现结果果然是:3392903

    四、延伸

    那我们看下面的代码,结果是啥呢?

    public class SwitchTest {
        public static void main(String[] args) {
            String param = null;
            switch (param="null") {
                case "null":
                    System.out.println("null");
                    break;
                default:
                    System.out.println("default");
            }
        }
    }

    image.gif

    答案是"null",这也侧面证实了官方文档所说的先执行swtich的”表达式“,对于String而言调用了hashCode函数。

    另外赋值语句为啥有返回值的问题参见:《Java赋值语句的返回值》

    五、总结

    俗话说”授人以鱼不如授人以渔“,本文讲述了三个主要方法一个是看源码,一个是看官方文档,一个是反汇编。

    另外我们学习Java编程的时候,遇到没把握的问题说明知识学的不够透彻,应该抓住时机搞透对应的知识点。

    看一些常见的基础图书入门以后,后面有时间一定要多看官方文档,多看源码

    有时间要学习Java反汇编命令,从更底层来学习Java编程语言

    创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

    相关文章
    |
    4小时前
    |
    安全 Go
    Go新手步步为赢:并发编程通信指南
    Go新手步步为赢:并发编程通信指南
    27 0
    |
    5小时前
    |
    程序员 数据安全/隐私保护
    编程之外,生活的美好航程
    编程之外,生活的美好航程
    |
    4小时前
    |
    Java
    Java 14 强势登场:Switch 表达式的进化之路
    Java 14 强势登场:Switch 表达式的进化之路
    30 0
    |
    11月前
    |
    人工智能 C语言
    大一新生必会的c语言五子棋!PVP,PVE,EVE模式都有,还有智能的AI部分,复盘等内容!一看就会的五子棋教程,确定不来看看吗?
    大一新生必会的c语言五子棋!PVP,PVE,EVE模式都有,还有智能的AI部分,复盘等内容!一看就会的五子棋教程,确定不来看看吗?
    86 0
    对比十二年前的vb代码,发现了点有意思的东西
    对比十二年前的vb代码,发现了点有意思的东西
    137 0
    对比十二年前的vb代码,发现了点有意思的东西
    |
    弹性计算 前端开发 IDE
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    |
    运维 Cloud Native 前端开发
    春天的第一个flag,12天打卡踏入云开发!
    蜕去厚厚的冬装,远离冬日的困乏与疲倦,是时候在春暖花开之际,跟随春天的步伐,为云开发能力增长设立新的Flag。为了助力开发者增长开发能力,实践开发项目,在3月末尾之际,云开发平台推出“12天云开发打卡挑战赛”,跟随阿里云开发专家,你将从9大课程了解如何搭建云原生应用!
    春天的第一个flag,12天打卡踏入云开发!
    |
    SQL 前端开发 程序员
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起(二)
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起(二)
    |
    弹性计算 前端开发 IDE
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起(一)
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起(一)
    |
    Java
    谁是代码界3%的王者?- 第五题Lock的简单解读
    谁是代码界3%的王者?- 第五题Lock的简单解读
    123 0