黑马程序员技术交流社区

标题: 谁是代码界3%的王者? [打印本页]

作者: 梦缠绕的时候    时间: 2019-7-1 13:43
标题: 谁是代码界3%的王者?
阿里技术的公众发了一篇文章《谁是代码界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 官方文档大法
https://docs.oracle.com/javase/s ... s-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");
        }
}
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

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




作者: 梦缠绕的时候    时间: 2019-7-1 13:43
有任何问题欢迎在评论区留言或者直接联系学姐
DKA-2018
作者: 晨大喵    时间: 2019-7-17 11:49
感谢分享~~




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2