示例代码:
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没有生效
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,执行拆箱操作肯定就会报空指针异常了。