今天碰到一个奇怪的空指针错误,场景大概是这样,从数据库中找出选修了某门课程的学生列表,然后筛选出不及格的考生,要求他们重新考试,部分代码如下

List<Student> list = StudentMapper.findByClass(classId); //找出选修课程的所有学生
list.stream()
    .filter(x -> x.getScore() < 60)
    ...

然而上面的代码,运行时会报空指针异常,由于错误信息包在 stream 里面并不是很清晰,我第一个想法就是 x 为空,故 x.getScore(0) 报空指针异常, 然而经过debug, 发现 list 均不是 null。然后我的方向往 stream api 的方向去找了,莫非全过滤调了,或者哪个 api 用错了,一番折腾,依旧没有解决。

最后,我注意到了 x.getScore() 返回的是整型Integer, int 的自动拆封装箱类型,原来不是 x 的值为空,而是x.getScore()的值为空,然后和 60 比较大小时,抛出空指针异常。

由于在其他场景自动拆封装箱使用的挺顺畅,以及编译期不报语法错误,Integerint互相比较时,很容易写成较简单的 == 形式,然后就出现了奇怪的空指针异常。

整型比较还有更不容易发现的坑,如下面的代码:

        Integer i1 = 120;
        Integer i2 = 120;
        Integer i3 = 130;
        Integer i4 = 130;

        System.out.println(i1==i2);  //true
        System.out.println(i3==i4);  //false
    }

是不是很惊奇?原因是 java 为优化性能,默认创建了值在(-128,128]这个范围内的整型装箱实例。

Integer类型的比较,实际还是遵循对象的比较方法,用equals方法,此时又不得不注意 null 空指针。

注意 Integer、Short、Byte、Character、Long、Double、Float、Boolean 这几个自动拆封装箱类型都有类似的问题,所以还是尽量用原生类型吧。