深究“HashMap在进行put操作时出现的空指针异常”

示例代码:

HashMap redisMap = new HashMap<>();
RMap hashMap = redissonComponent.getMap(ORDER_REFUND_RECORD_SNAPSHOT);
redisMap = (HashMap) hashMap.get(model.getManagerId().toString());
redisMap.put(model.getId(), refundType);

一、上述代码在运行过程中偶发空指针异常,在看到日志抛错的时候我试想了可能出现的几种情况:

假设1.model.getId()抛出异常,Id这个参数为null

假设2.Id或者refundType中存在数据类型为int的情况,与map中声明的Integer的数据类型不符

假设3.赋值后的redisMap为null,使之前初始化的redisMap没有生效

二、接下来进入debug模式

debug1.首先debug到第四行,观察到id与refundType二者皆有值,且数据类型均为Integer,因此可以排除掉假设1和假设二。

debug2.接着debug到第二行,发现程序从Redis中获取到的hashMap为null,理所应当的是,debug到第三行代码的时候,redisMap的结果也为null,那么此时基本可以断定假设3是正确的。

三、如何解决此次问题

修正代码

HashMap redisMap = new HashMap<>();
RMap hashMap = redissonComponent.getMap(ORDER_REFUND_RECORD_SNAPSHOT);
redisMap = (HashMap) hashMap.get(model.getManagerId().toString());
if (ObjectUtils.isEmpty(redisMap)) {
    redisMap = new HashMap<>();
}
redisMap.put(model.getId(), refundType);

    根据上面的debug2,我在redisMap第一次赋值后加入了逻辑判断,如果redisMap被赋予了null值,就需要重新对redisMap进行初始化,并分配内存

修正代码后,程序运行中没有再次出现空指针异常。此时我可以得出结论:

1.HashMap如果只初始化而不为其分配内存,在进行put操作时,会出现空指针异常。

2.HashMap在初始化且分配内存后,如果对其进行赋予null的操作,接着进行put操作时,依然会出现空指针异常。

四、知识扩展

最后回顾一下解决问题的思路,首先我假设了三种出现此类问题的情况,其次去程序中进行调试,最后给出解决方案。

作为知识的扩展,我们回到假设2,为什么我会做如此假设呢?

1.如果我们put的数据是int类型,此时会有一个装箱的过程,也就是调用Integer的静态方法valueOf,将int装箱为Integer

public static Integer valueOf(int i) {
      if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
      return new Integer(i);
  }

2.执行map的put方法

 public V put(K key, V value) {
      return putVal(hash(key), key, value, false, true);
  }

     可以看到put方法其实是有返回值的,只不过我们通常不会在意它。如果当前put的key的value在put之前,map里面是存在的,那么就返回之前已经存在的value对象,如果不存在那么就返回null。

这里我们要注意:Map里面只能存放对象,不能存放基本类型,例如int,需要使用Integer。

3.由于当前int类型的值是初次put,map里是不存在对应的value的,那么此时返回的Integer对象肯定是null。此时需要为put方法的返回值赋值,那么就需要对Integer对象执行拆箱操作。

  Integer拆箱操作

 public int intValue() {
return value;
}

此时返回的Integer对象是null,执行拆箱操作肯定就会报空指针异常了。

 

你可能感兴趣的:(java相关)