20k招的工程师刚加入团队,4行代码写3个NPE异常,服了!

简介: 20k招的工程师刚加入团队,4行代码写3个NPE异常,服了!


来源:juejin.cn/post/

7031445206152577061

image.png


一、前言

Hello 大家好,今天带来一个真实案例,让大家更深刻的理解空指针异常。

公司刚入职了一名中级Java开发,经过一个星期的适应学习,各方面表现还不错,于是分配了一个小的迭代给新人做。

需求很简单,把从第三方拉取的数据匹配到自身公司后台设置的渠道后,聚合到一个列表中,批量入库。

然而就在匹配的逻辑中,上线后报了个NPE,这是作为一名中级开发不应犯的简单错误,新人被我狠狠的训了,记生产事故一次。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

二、事故重现

伪代码

说明:伪代码并非真实线上代码,只是为了更方便,更形象的重现事故现场而编写的;真实的业务场景往往更加复杂,NPE的漏洞隐藏在更深处,不易code view出来,也不易测试出来;生产环境NPE是较常见的异常,希望大家不要纠结为什么测试没测出来,关键还是通过这样一个案例了解NPE的原因和解决方案。

// 后台设置的渠道
String channelNo = channelDao.getOne().getChannelNo();
// 第三方拉取的数据
List<ThirdData> thirdDataList = httpClientUtils.getThirdDatas(DateUtils.today());
// 匹配过滤
thirdDataList.stream().filter(o ->channelNo.equals(o.getChannelNo())).collect(Collectors.toList());
// 批量入库
thirdDataDao.saveAll(thirdDataList);
分析与解决

有经验、技术扎实的同学看到这里应该或多或少能发现问题了。其实啊,这四段代码是作者精心设计的,可谓是卧龙凤雏 😀😀。

短短四行代码居然凑齐了3个NPE,我枯了/(ㄒoㄒ)/~~

我们逐行分析 :

第一行分析

channelDao.getOne()如果返回为null,那么调用getChannelNo()会报NPE。

解决办法

1、使用防御性编程,提前返回(需根据具体业务场景而定)
// 如果channelNo是方法逻辑执行的必须元素,推荐用此方法
Channel channel = channelDao.getOne();
if (channel == null) {
    return;
}
2、使用三目运算,返回空字符串("")
// 返回兜底的空字符串
String channelNo = channelDao.getOne() == null ? "" : channelDao.getOne().getChannelNo();
3、使用Optional函数,返回空字符串("")
String channelNo = Optional.ofNullable(channelDao.getOne()).orElse("");

第三行分析(1)

thirdDataList如果为null,那么调用stream()会报NPE。

通过下面的源码截图就能知道原因:

image.png

image.png

解决办法

1、使用防御性编程,提前返回(推荐)
// 推荐使用集合工具类判空
if (CollectionUtils.isEmpty(thirdDataList)) {
    return;
}
2、使用if条件语句包裹(不推荐)
if (CollectionUtils.isNotEmpty(thirdDataList)) {
    // 执行后面的逻辑
}

第三行分析(2)

channelNo如果返回为null,那么执行channelNo.equals(o.getChannelNo())会报NPE。

我们知道,按Java的规范String的equals()方法的调用,要求左边是确定值,就是为了避免调用方为null的情况。然而这里调用方和equals的入参都是变量,这种情况该怎么办呢?

1、再加一句判断:
channelNo != null && channelNo.equals(o.getChannelNo())
2、其实可以用java.uti包下的Objects类的equals方法
Objects.equals(channelNo, o.getChannelNo())

看源码一目了然,该方法对左边的对象做了非空判断

image.png

3、用其他开源的工具类库或者自己实现
如:
org.apache.commons.lang3.StringUtils
cn.hutool.core.util.StrUtil;


基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

最后

在这里要推荐一款IDEA的插件:

SonarLint

image.png

能动态的帮您检查代码漏洞,像NPE这种代码风险都会给于相应的提示。

SonarLint还有一个大名鼎鼎的服务端叫SonarQube。

相关文章
|
1月前
|
人工智能 自然语言处理 监控
大模型时代来了,新一代推荐AI客服产品有哪些必看指标?
本文剖析大模型时代AI客服选型范式变革:从“能否答对”转向“能否办完”。提出六大核心评估指标——任务闭环率、多轮对话能力、全渠道上下文保持率、工单闭环能力、安全合规性与三年TCO,以阿里云瓴羊Quick Service为实践样本,强调真实场景POC验证的重要性。
|
3月前
|
编解码 运维 算法
iOS云手机H.264与H.265编解码技术对比 2026远程低延迟传输选型科普
云手机流畅度关键在视频编码!H.264兼容广但耗流高、延迟大;H.265硬编码码率降50%、延迟≤30ms,苹果原生优化,省流又跟手。2026年中高端云手机已全面转向H.265,iOS用户务必认准硬件编码方案。(239字)
|
7月前
|
SQL 关系型数据库 MySQL
Linux 安装 mysql | 创建mysql 工具连接不上的解决办法
MySQL本地安装后远程连接失败?常见原因:防火墙未放行23306/3306端口,或root用户仅限localhost访问。需依次检查:①系统及云平台安全组端口策略;②MySQL用户权限,执行ALTER USER+CREATE USER+GRANT授权并刷新权限。(239字)
|
10月前
|
缓存 API UED
Vue3性能调优实战:5个关键技巧让你的应用飞起来!
Vue3性能调优实战分享,涵盖shallowRef、v-memo、异步组件、markRaw、watchEffect等关键优化技巧。
Vue3性能调优实战:5个关键技巧让你的应用飞起来!
|
传感器 算法 定位技术
技术心得记录:常用的滤波算法
技术心得记录:常用的滤波算法
1227 0
|
缓存 网络协议 安全
dns被劫持怎么修复?6种常用修复方法
dns被劫持怎么修复?6种常用修复方法
|
存储
ArcMap报错ERROR 999999的一种解决方法
本文介绍一种解决ArcGIS中ERROR 999999报错(Configuration RasterCommander ImageServer can not be started)的方法~
1459 1
ArcMap报错ERROR 999999的一种解决方法
|
移动开发 小程序 安全
DingTalk「开发者说」— 钉钉数据授权开发实战
随着《中华人民共和国网络安全法》的实施,对个人信息的保护越来越严格,钉钉遵守安全法要求,对钉钉上个人信息的收集、使用、共享有严格的把关。本次分享主要介绍钉钉如何做个人数据把关,以及在个人选择同意原则下,如何对个人数据做授权流转,以及实践演练。 DingTalk「开发者说」是钉钉开发者最新上线的开发者栏目,联合阿里云ACE团队,分享钉应用开发解决方案、技术更新、实战技巧,致力于成为钉钉与开发者的桥梁与纽带,让更多的钉钉开发者传播技术、提升技能、分享观点。在数字化变革的时代,“云钉一体”“钉钉全面开放”战略之后,希望钉钉技术可以持续激发开发者的创造力,为组织数字化赋能。
3222 4
DingTalk「开发者说」— 钉钉数据授权开发实战

热门文章

最新文章