点击上方的“宇道源代码”,选择“”
谁关心第一波或第二波?
能掀起的浪花才是好浪花!
每天10点33分更新文章,每天都会掉一点头发……
源代码精品专栏
大家好,今天我给大家带来一个真实的案例,帮助大家更深入的理解空指针异常。
公司刚招了一位中级Java开发人员,经过一周的适应和学习,他各方面表现都还不错,所以就给新人安排了一个小迭代的工作。
需求很简单,把第三方拉来的数据和公司后台设置的渠道匹配之后,汇总成一个列表,然后分批存储。
但是在匹配逻辑上,上线后报了NPE,这是一个中级开发人员不应该犯的简单错误,我狠狠地训斥了新人一顿,并记录为生产事故。
基于Boot+Plus+Vue&的后端管理系统+用户小程序,支持RBAC动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
注意:伪代码并非真实的线上代码,只是为了更方便形象的复现事故现场而写的。真实的业务场景往往更加复杂,NPE漏洞隐藏的更深,难以查看和测试。NPE是生产环境中常见的异常,希望大家不要纠结为什么没测试出来,关键还是要通过这样的案例,了解NPE的成因和解决办法。
// 后台设置的渠道
String channelNo = channelDao.getOne().getChannelNo();
// 第三方拉取的数据
List thirdDataList = httpClientUtils.getThirdDatas(DateUtils.today());
// 匹配过滤
thirdDataList.stream().filter(o ->channelNo.equals(o.getChannelNo())).collect(Collectors.toList());
// 批量入库
thirdDataDao.saveAll(thirdDataList);
有经验、功底扎实的同学看到这里应该或多或少能发现问题所在,其实这四段代码都是作者精心设计的,可谓是龙凤呈祥。
短短四行代码就有3个NPE,累死我了/(ㄒoㄒ)/~~
我们来逐行分析一下:
如果 .() 返回 null,则调用 () 将导致 NPE。
// 如果channelNo是方法逻辑执行的必须元素,推荐用此方法
Channel channel = channelDao.getOne();
if (channel == null) {
return;
}
// 返回兜底的空字符串
String channelNo = channelDao.getOne() == null ? "" : channelDao.getOne().getChannelNo();
String channelNo = Optional.ofNullable(channelDao.getOne()).orElse("");
如果为空,调用()会报NPE。
以下源代码截图显示了原因:
// 推荐使用集合工具类判空
if (CollectionUtils.isEmpty(thirdDataList)) {
return;
}
if (CollectionUtils.isNotEmpty(thirdDataList)) {
// 执行后面的逻辑
}
如果返回值为空,执行.(o.())将导致NPE。
我们知道,按照 Java 规范,() 方法调用时,要求左边有一个确定的值,以避免出现 为 null 的情况。但这里 和输入参数都是变量,这种情况该怎么办呢?
channelNo != null && channelNo.equals(o.getChannelNo())
Objects.equals(channelNo, o.getChannelNo())
从源码中可以清楚的看出,这个方法对左边的对象做了一个非空的判断。
如:
org.apache.commons.lang3.StringUtils
cn.hutool.core.util.StrUtil;
基于++Vue&++用户小程序的后端管理系统,支持RBAC动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
这里给大家推荐一个IDEA插件:
能够帮助您动态检查代码漏洞,并对NPE等代码风险给出相应提示。
还有一个著名的服务器叫做。
欢迎加入我的知识星球,一起讨论架构、交流源码,加入请长按下方二维码:
源代码分析在知识星球更新如下:
近期更新的《云道2.X入门》系列文章超过101篇文章,涵盖了ES、分片、读写分离、权限、性能测试等内容。
提供近3万行代码的示例以及超4万行代码的电商微服务项目。