更新时间:2019年07月26日 11时18分54秒 来源:黑马程序员论坛
阿里技术的公众发了一篇文章《谁是代码界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 --------------------- 原文: |