没想到 switch 语句居然有这么一个坑

  • 在开发测试的过程中,发现一个崩溃,具体堆栈如下:
Process: xxx.xxx.xxx, PID: 27468
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
    at xxx.xxx.xxx.xxx.MessageFragment.getSexType(MessageFragment.java:57)
    at xxx.xxx.xxx.xxx.MessageFragment.access$100(MessageFragment.java:22)
    at xxx.xxx.xxx.xxx.MessageFragment$1.onClick(MessageFragment.java:50)
    at android.view.View.performClick(View.java:7514)
    at android.view.View.performClickInternal(View.java:7491)
    at android.view.View.access$3600(View.java:846)
    at android.view.View$PerformClick.run(View.java:28733)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:236)
    at android.app.ActivityThread.main(ActivityThread.java:8134)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
  • 发现 MessageFragment 类中的 57 行是一个 String 调用 hashCode 的时候报空指针了,我回去看了一眼代码
  • 这个类 57 行中压根没有调用过 hashCode?看完我顿时陷入了沉思

  • 过了一会儿我的小脑袋瓜灵机一动,是不是 getSexTypesexString 参数为空导致的?但是如果 sexString 参数为空,应该走 default 语句才对啊?怎么可能会调用 hashCode 方法呢?更别说报空指针异常了?先不管它三七二十八,我先试验一下,实践出真理。

  • 于是乎,我将这个方法的参数直接改成了 null,结果一运行就真的复现了这个问题

  • 那么问题来了,我明明没调用 hashCode 方法,为什么它就是提示我有呢?

  • 既然能复现,那么应该从哪里从手这个问题?我的小脑瓜又灵机一动,是不是编译器在编译的过程中修改了我的代码?我心想这种事情不太可能发生,但是实践出真理,先试了再说

  • 说干就干,我们都知道 apk 的类都是在 dex 文件里面的,而 dex 是由多个 class 类合并而成,那么现在就让我们在项目中的 build 文件夹中搜索刚刚出现崩溃的那个类

  • 并把它拖动到 Android Studio 打开看看看
  • 结果真的被我猜对了,此时此刻我的心情是五味杂陈,沉思许久后发出了感叹:居然还能这么玩,这波操作真是让我开了眼

  • 原来 Java JDK 1.8 支持 switch 判断 String 类型是这么来的,本质上还是走的 switch 判断 int 类型的逻辑,调用 hashCode 方法只不过想得出字符串的哈希码,而字符串的哈希码是通过将 String 分解成一个个 char,然后通过运算得出,所以相同内容的 String 对象的 hasCode 是一样的。

  • 既然知道了这其中的原理,那么刚刚的崩溃问题要怎么处理比较好?我认为有两种方式

  • 第一种:用 String 作为 switch 的判断条件时,应当先进行判空

private int getSexType(String sexString) {
    if (sexString == null) {
        return 0;
    }
    int sexType = 0;
    switch (sexString) {
        case "男":
            sexType = 0;
            break;
        case "女":
            sexType = 1;
            break;
        default:
            break;
    }
    return sexType;
}
  • 第二种:可以换成 if else + equals 语句来判断
private int getSexType(String sexString) {
    if ("男".equals(sexString)) {
        return 0;
    } else if ("女".equals(sexString)) {
        return 1;
    }
    return 0;
}
  • 至于用哪一种,这里不做强制要求,大家可以看着来

Android 技术讨论 Q 群:10047167

你可能感兴趣的:(没想到 switch 语句居然有这么一个坑)