java浪漫代码简单_谁是Java代码界3%的王者?第三题switch问题简单解读

79e9bb25153424b0aef03c7248c7d2d3.png

一、背景

阿里技术的公众发了一篇文章《谁是代码界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");
        }
    }
}

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

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

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

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

三、上法宝

3.1 源码大法

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

3.2 官方文档大法

Chapter 14. Blocks and Statements

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");
        }
}

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
}

关键点

第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
}

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

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

switch语句表达式大致等价于

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

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

另外我们打印

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

发现结果果然是: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");
        }
    }
}

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

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

五、总结

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

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

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

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


原文链接:https://blog.csdn.net/w605283073/article/details/93140548

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值