谁是代码界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编程语言

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

    相关文章
    |
    8月前
    |
    安全 Go
    Go新手步步为赢:并发编程通信指南
    Go新手步步为赢:并发编程通信指南
    60 0
    |
    4月前
    |
    开发框架 JavaScript 前端开发
    |
    5月前
    |
    Shell 数据处理 C++
    【震撼揭秘】Python正则VS Shell正则:一场跨越编程边界的史诗级对决!你绝不能错过的精彩较量,带你领略文本处理的极致魅力!
    【8月更文挑战第19天】正则表达式是文本处理的强大工具,在Python与Shell中有广泛应用。两者虽语法各异,但仍共享许多基本元素,如`.`、`*`及`[]`等。Python通过`re`模块支持丰富的功能,如非捕获组及命名捕获组;而Shell则依赖`grep`、`sed`和`awk`等命令实现类似效果。尽管Python提供了更高级的特性和函数,Shell在处理文本文件方面仍有其独特优势。选择合适工具需根据具体需求和个人偏好决定。
    45 1
    |
    5月前
    |
    开发者 Java
    JSF EL 表达式:乘技术潮流之风,筑简洁开发之梦,触动开发者心弦的强大语言
    【8月更文挑战第31天】JavaServer Faces (JSF) 的表达式语言 (EL) 是一种强大的工具,允许开发者在 JSF 页面和后台 bean 间进行简洁高效的数据绑定。本文介绍了 JSF EL 的基本概念及使用技巧,包括访问 bean 属性和方法、数据绑定、内置对象使用、条件判断和循环等,并分享了最佳实践建议,帮助提升开发效率和代码质量。
    61 0
    |
    8月前
    |
    Java
    Java 14 强势登场:Switch 表达式的进化之路
    Java 14 强势登场:Switch 表达式的进化之路
    109 0
    |
    机器学习/深度学习 安全 算法
    华硕编程竞赛11月JAVA专场 A题自由弹簧 题解
    华硕编程竞赛11月JAVA专场 A题自由弹簧 题解
    |
    开发框架 Java 中间件
    java程序设计与j2ee中间件技术/软件开发技术(III)-实验五-实现一个简单的购物车功能,使用JSP内置对象实现猜数字的小游戏
    java程序设计与j2ee中间件技术/软件开发技术(III)-实验五-实现一个简单的购物车功能,使用JSP内置对象实现猜数字的小游戏
    233 7
    java程序设计与j2ee中间件技术/软件开发技术(III)-实验五-实现一个简单的购物车功能,使用JSP内置对象实现猜数字的小游戏
    |
    C语言
    C语言练级之路(num6)10个基础小题目等你来挑战
    首先今天有点迟了,原因就是去看了一下别的大佬的有关十六进制转八进制的博客,看的我现在人都有点不正常,要不是我目前还不会写,我觉得有的博主的写法我属实不能理解,等我哪天研究会了,……
    |
    弹性计算 前端开发 IDE
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    High&NewTech:新物种?这是一种不需要写代码的程序猿,这事,得从Ta们掌握了 iVX工具(首个无代码编程语言)说起……
    【硬着头皮】switch很少看到,还是写 if 吧?
    【硬着头皮】switch很少看到,还是写 if 吧?
    106 0
    【硬着头皮】switch很少看到,还是写 if 吧?

    热门文章

    最新文章