记录一次空指针异常:getOrDefault方法与对象转字符串

72 阅读2分钟

一天下午同事找过来说一个稳定跑了两年的更新合同状态的定时任务,今天突然就报错了,还是报空指针异常,这就奇怪了,这个任务经常跑,看了业务代码也没有更新记录,咋报错了呢

业务逻辑也是很简单的,优先使用ADate,没有的话则使用BDate作为参数,

public class Main {
    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String,Object> m1 = new HashMap<>();
        m1.put("ADate",null);//ADate的在数据库中的值可能为null
        m1.put("BDate","2022-02-02");//BDate的在数据库中的必有值
        list.add(m1);
        list.forEach(tempMap -> {
            //空指针异常
            String showDate = tempMap.getOrDefault("ADate",tempMap.get("BDate")).toString();
        });

    }
}

忽略一段被同事带跑偏的讨论,直接看代码中的两个问题

1.getOrDefault方法

同事使用的时候一直以为是key或者value为null的话就会走默认值。实际上我们看HashMap的源码

image.png 源码是根据Node节点来判断null的,换言之。只要Map中有这个key,无论key对应的value是什么都会返回这个value。Map中不包含这个key才会返回defaultValue

2.对象转字符串问题

toString这个方法好用也是好用,危险也是危险,因为需要确保调用对象本身不为null才行。否则就会出现NPE异常。同事觉得map.getOrDefault().toString()写的特别丝滑,就直接用了。我们常用的话一般就是4种

方法使用注意事项
toStringobject.toString()有空指针风险,少用
类型转换(String)object没有空指针问题,可能会有ClassCastException类型转换异常
String.valueOfString.valueOf(object)安全,但是需要注意null转换之后会变成字符串"null"
拼接字符串"" + object安全,但是需要注意null转换之后会变成字符串"null"

如果需要的效果是null对象转成""空字符串的话,推荐使用hutool框架的Convert工具

Object obj = null;
String s = Convert.toStr(obj, "");

好了,来到最后一个问题,为什么这个任务稳定跑了两年,今天才出现问题呢,肯定是有人修改过什么,并且这个修改是最近的,是全局的。翻一下提交记录,在Mybatis配置文件中发现这么一句话

<!--解决,查询返回结果含null没有对应字段值问题-->  
<setting name="callSettersOnNulls" value="true"/>

至此,一切都能合理解释起来了。这个工程之前是没有上面这个配置的。所以返回的Map结果集,如果数据库中值为null的话,Map中也不会出现对应的字段值。所以同事之前使用getOrDefault是没问题的,误打误撞跑起来了。但是后面加了这个配置,就会有key对应value值为null的情况。null后面再加一个toString,就出现了空指针异常。

真就是代码以一种奇怪的形式跑起来。当然这也给了我们提醒。具有全局性修改的配置,一定一定要慎重修改,要不然线上就会出问题,最好的办法还是建立项目流程,强约束让大家都遵守某个开发规范,配置统一就能减少这种乌龙情况