A “NullPointerException“ could be thrown; “body“ is nullable here.
一、背景在编写一个用于处理文件下载的 Spring 工具方法时遇到了静态代码扫描工具SonarQube / IDEA Inspection抛出的一条告警A NullPointerException could be thrown; body is nullable here.对应的问题代码如下javaprivate ResponseEntitybyte[] toDownloadResponse(ResponseEntitybyte[] entity) { HttpHeaders headers new HttpHeaders(); MediaType contentType entity.getHeaders().getContentType(); if (contentType ! null) { headers.setContentType(contentType); } String contentDisposition entity.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION); if (contentDisposition ! null) { headers.set(HttpHeaders.CONTENT_DISPOSITION, contentDisposition); } byte[] body entity.getBody() ! null ? entity.getBody() : new byte[0]; headers.setContentLength(body.length); // ⚠️ 告警位置 return new ResponseEntity(body, headers, entity.getStatusCode()); }乍一看这段代码的意图非常明确如果entity.getBody()返回 null就用空数组兜底。既然已经做了非空判断为什么工具还会认为body可能为 null从而在body.length处抛出 NPE 呢下面我们把这个问题彻底讲清楚。二、告警的根本原因三元表达式里调用了两次getBody()问题的关键其实藏在这一行javabyte[] body entity.getBody() ! null ? entity.getBody() : new byte[0]; // ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ // 第 1 次调用 第 2 次调用这行代码里entity.getBody()被调用了两次第一次在条件判断! null里用来判断是否为 null第二次在真分支里用来取出实际的字节数组。对于人类阅读来说我们默认ResponseEntity一旦构造完成getBody()每次调用都会返回同一个对象所以两次调用的结果必然一致——第一次非 null第二次也一定非 null。但静态分析工具并不这么认为。它遵循的是一种非常保守的推理逻辑getBody()是一个方法调用其返回值在方法签名上并没有被标注为NonNull因此在工具眼里它的返回值可能为 null两次独立的getBody()调用工具无法保证它们返回同一个引用工具并不理解ResponseEntity内部就是简单返回一个final字段于是它认为第二次调用完全可能返回 null那么三元表达式的真分支就会把 null 赋给body一旦body为 null后面body.length就会抛NullPointerException。这类问题在软件工程中有一个经典的名字——TOCTOUTime-of-Check to Time-of-Use检查时与使用时不一致。虽然这里并不是真正的时间竞争但在静态分析的语义层面它属于同一类检查后再次使用可能变化的值的模式。三、修复方案3.1 核心思路只调用一次getBody()将结果保存到局部变量后续判断和赋值都基于这个局部变量。这样工具就能沿着两条分支精确推理rawBody ! null→body rawBody非 nullrawBody null→body new byte[0]也非 null。无论走哪条分支body都必然非 null告警自然消失。3.2 方案一提取局部变量推荐改动最小、最直观兼容所有 Java 版本javaprivate ResponseEntitybyte[] toDownloadResponse(ResponseEntitybyte[] entity) { HttpHeaders headers new HttpHeaders(); MediaType contentType entity.getHeaders().getContentType(); if (contentType ! null) { headers.setContentType(contentType); } String contentDisposition entity.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION); if (contentDisposition ! null) { headers.set(HttpHeaders.CONTENT_DISPOSITION, contentDisposition); } byte[] rawBody entity.getBody(); // 只取一次 byte[] body rawBody ! null ? rawBody : new byte[0]; headers.setContentLength(body.length); return new ResponseEntity(body, headers, entity.getStatusCode()); }3.3 方案二Objects.requireNonNullElseJava 9JDK 9 提供了Objects.requireNonNullElse语义就是为空则用默认值表达更简洁javabyte[] body Objects.requireNonNullElse(entity.getBody(), new byte[0]);

相关新闻

目标检测边界框坐标转换:xywh与xyxy互转及避坑指南

目标检测边界框坐标转换:xywh与xyxy互转及避坑指南

搞目标检测的兄弟,应该都有过这种经历:从网上扒下来的数据集,标注格式是xmin,ymin,width,height,比如[100, 150, 200, 300];但你的训练代码里,DataLoader读进来却要求xmin,ymin,xmax,ymax,也就是…

2026/9/23 18:20:37 阅读更多 →
3个致命坑,配置半天才懂激光原理,一文搞懂避坑指南

3个致命坑,配置半天才懂激光原理,一文搞懂避坑指南

3个致命坑,配置半天才懂激光原理,一文搞懂避坑指南 配个激光模块,代码跑不通,环境卡半天?别慌。 这行干了十年,见过太多人死在“配置”上。 其实, 激光原理 这东西,理论深奥,但工程落地就那几件事。 今天不聊高深物理,只讲怎么把坑填平。…

2026/9/23 18:20:37 阅读更多 →
iTerm2设置command+左←右→箭头在命令之间快速切换

iTerm2设置command+左←右→箭头在命令之间快速切换

command, 打开设置-> keys 增加快捷键 f 向前,b 向后 参考: 5 个必备的 iTerm 命令快捷键 - 腾讯云开发者社区-腾讯云5 个必备的 iTerm 命令快捷键。除了这 2 个之外,还有 3 个快捷键也会高频使用:「Ctrl A」到达命令开头「C…

2026/9/23 18:20:37 阅读更多 →

最新新闻

Springboot集成Tesseract OCR:从图片到字段的落地实践

Springboot集成Tesseract OCR:从图片到字段的落地实践

简介:一份面向Spring Boot开发者的OCR图片文字识别实现方案,聚焦如何整合Tesseract开源识别引擎完成图片文本自动提取,适合有Java基础、需要在文档扫描、证照识别等场景落地识别功能的读者参考。资源以PDF格式打包,共1个文件&…

2026/9/23 21:05:49 阅读更多 →
PaddleSpeech ASR 识别解码模块 paddlespeech.s2t.decoders.recog 源码深度解析

PaddleSpeech ASR 识别解码模块 paddlespeech.s2t.decoders.recog 源码深度解析

PaddleSpeech ASR 识别解码模块 paddlespeech.s2t.decoders.recog 源码深度解析 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Veri…

2026/9/23 21:05:49 阅读更多 →
观赏虾突然死亡原因分析与水质管理指南

观赏虾突然死亡原因分析与水质管理指南

1. 养虾新手的第一道坎:突然死亡事件分析那天早上掀开鱼缸盖子的场景至今难忘——昨晚还活蹦乱跳的观赏虾,今早突然横七竖八地躺在缸底。这种突如其来的死亡事件,几乎每个养虾人都会经历。不同于鱼类养殖,虾类对水质变化更为敏感&…

2026/9/23 21:05:49 阅读更多 →
papi酱最火的视频新手避坑指南与技术方案对比

papi酱最火的视频新手避坑指南与技术方案对比

papi酱最火的视频新手避坑指南与技术方案对比 看到满屏红色的 StackTrace,报错信息像天书一样滚过屏幕,是不是瞬间头大?别慌,这几乎是每个接触后端或全栈开发新手的必经之路。很多时候,你以为自己在看代码,其实是在看一场关于“papi…

2026/9/23 21:04:49 阅读更多 →
netstat 网络排查与安全分析速查指南(jaywcjlove/reference)

netstat 网络排查与安全分析速查指南(jaywcjlove/reference)

文档知识库教程开发工具 【免费下载链接】reference 为开发人员分享快速参考备忘清单(速查表) 项目地址: https://gitcode.com/jaywcjlove/reference 点击查看 免费下载 本指南以开源速查仓库 jaywcjlove/reference 中 docs/netstat.md 为骨架,系统梳理…

2026/9/23 21:04:49 阅读更多 →
PaddleSpeech DeepSpeech2 卷积下采样模块 `paddlespeech.s2t.models.ds2.conv` 源码级解析

PaddleSpeech DeepSpeech2 卷积下采样模块 `paddlespeech.s2t.models.ds2.conv` 源码级解析

人工智能语音音频 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword…

2026/9/23 21:04:49 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →