一、前言
Hello 大家好,我是師長(zhǎng),今天帶來(lái)一個(gè)真實(shí)案例,讓大家更深刻的理解空指針異常。
公司剛?cè)肼毩艘幻屑?jí)Java開(kāi)發(fā),經(jīng)過(guò)一個(gè)星期的適應(yīng)學(xué)習(xí),各方面表現(xiàn)還不錯(cuò),于是分配了一個(gè)小的迭代給新人做。
需求很簡(jiǎn)單,把從第三方拉取的數(shù)據(jù)匹配到自身公司后臺(tái)設(shè)置的渠道后,聚合到一個(gè)列表中,批量入庫(kù)。
然而就在匹配的邏輯中,上線后報(bào)了個(gè)NPE,這是作為一名中級(jí)開(kāi)發(fā)不應(yīng)犯的簡(jiǎn)單錯(cuò)誤,新人被我狠狠的訓(xùn)了,記生產(chǎn)事故一次。
二、事故重現(xiàn) 1、偽代碼
說(shuō)明:偽代碼并非真實(shí)線上代碼,只是為了更方便,更形象的重現(xiàn)事故現(xiàn)場(chǎng)而編寫(xiě)的;真實(shí)的業(yè)務(wù)場(chǎng)景往往更加復(fù)雜,NPE的漏洞隱藏在更深處,不易code view出來(lái),也不易測(cè)試出來(lái);生產(chǎn)環(huán)境NPE是較常見(jiàn)的異常,希望大家不要糾結(jié)為什么測(cè)試沒(méi)測(cè)出來(lái),關(guān)鍵還是通過(guò)這樣一個(gè)案例了解NPE的原因和解決方案。
// 后臺(tái)設(shè)置的渠道
String channelNo = channelDao.getOne().getChannelNo();
// 第三方拉取的數(shù)據(jù)
List thirdDataList = httpClientUtils.getThirdDatas(DateUtils.today());
// 匹配過(guò)濾
thirdDataList.stream().filter(o ->channelNo.equals(o.getChannelNo())).collect(Collectors.toList());
// 批量入庫(kù)
thirdDataDao.saveAll(thirdDataList);
2、分析與解決有經(jīng)驗(yàn)、技術(shù)扎實(shí)的同學(xué)看到這里應(yīng)該或多或少能發(fā)現(xiàn)問(wèn)題了。其實(shí)啊,這四段代碼是作者精心設(shè)計(jì)的,可謂是臥龍鳳雛。
短短四行代碼居然湊齊了3個(gè)NPE,我枯了~~
我們逐行分析:
3、第一行分析
channelDao.getOne()如果返回為null,那么調(diào)用getChannelNo()會(huì)報(bào)NPE。
4、解決辦法
1、使用防御性編程,提前返回(需根據(jù)具體業(yè)務(wù)場(chǎng)景而定)
// 如果channelNo是方法邏輯執(zhí)行的必須元素,推薦用此方法
Channel channel = channelDao.getOne();
if (channel == null) {
return;
2、使用三目運(yùn)算,返回空字符串("")
// 返回兜底的空字符串
String channelNo = channelDao.getOne() == null ? "" : channelDao.getOne().getChannelNo();
3、使用Optional函數(shù),返回空字符串("")
String channelNo = Optional.ofNullable(channelDao.getOne()).orElse("");
5、第三行分析(1)
thirdDataList如果為null,那么調(diào)用stream()會(huì)報(bào)NPE。
通過(guò)下面的源碼截圖就能知道原因:
6、解決辦法
1、使用防御性編程,提前返回(推薦)
// 推薦使用集合工具類判空
if (CollectionUtils.isEmpty(thirdDataList)) {
return;
2、使用if條件語(yǔ)句包裹(不推薦)
if (CollectionUtils.isNotEmpty(thirdDataList)) {
7、第三行分析(2)
// 執(zhí)行后面的邏輯
channelNo如果返回為null,那么執(zhí)行channelNo.equals(o.getChannelNo())會(huì)報(bào)NPE。
我們知道,按Java的規(guī)范String的equals()方法的調(diào)用,要求左邊是確定值,就是為了避免調(diào)用方為null的情況。然而這里調(diào)用方和equals的入?yún)⒍际亲兞浚@種情況該怎么辦呢?
1、再加一句判斷:
channelNo != null && channelNo.equals(o.getChannelNo())
2、其實(shí)可以用java.uti包下的Objects類的equals方法
Objects.equals(channelNo, o.getChannelNo())
看源碼一目了然,該方法對(duì)左邊的對(duì)象做了非空判斷
3、用其他開(kāi)源的工具類庫(kù)或者自己實(shí)現(xiàn)
如:
org.apache.commons.lang3.StringUtils
cn.hutool.core.util.StrUtil;
原文鏈接:juejin.cn/post/7031445206152577061
原文作者:l拉不拉米
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.