首页/新闻资讯/正文详情

A “NullPointerException“ could be thrown; “body“ is nullable here.

发布时间:2026/9/23 18:21:36 来源:云帆数科 栏目:资讯中心
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]);

相关推荐

mhz原理详解
mhz原理详解

3个关键步骤搞定CPU频率监测,性能优化不再卡半天 配置环境就卡半天?明明代码逻辑没问题,一跑起来 CPU 占用率忽高忽低,甚至直接飙红,排查半天发现是频率动态调节导致的性能抖动。在高性能计算或实时系统中,这种不稳定性是性能优化的大敌。很多… · 2026/9/23 18:21:29

5年老兵复盘:简历大赛技术栈避坑指南与最佳实践
5年老兵复盘:简历大赛技术栈避坑指南与最佳实践

5年老兵复盘:简历大赛技术栈避坑指南与最佳实践 版本升级后 API 全变了,这是每个开发者在维护老旧项目或接手新代码库时最头疼的问题。特别是当团队内部对于“简历大赛”这类高并发、高交互场景的技术选型缺乏统一标准时,代码的脆弱性会被无限放大。… · 2026/9/23 18:21:29

劳动节活动源码拆解:保姆级教程搞定原理面试
劳动节活动源码拆解:保姆级教程搞定原理面试

劳动节活动源码拆解:保姆级教程搞定原理面试 面试时被问劳动节活动实现原理,脑子一片空白?别慌,这期保姆级教程带你深挖核心代码,彻底搞懂底层逻辑,告别死记硬背。… · 2026/9/23 18:21:29

6S换电池实战:2026最新调试避坑与代码解析
6S换电池实战:2026最新调试避坑与代码解析

6S换电池实战:2026最新调试避坑与代码解析 复制来的代码跑不通不知道怎么调?别急,这几乎是每个刚接触嵌入式或物联网开发者的噩梦。面对 6S换电池 这种涉及高电压安全的场景,2026最新… · 2026/9/23 20:04:40

Eclipse Mosquitto 集成 Let‘s Encrypt:deploy 钩子脚本与证书热重载完整指南
Eclipse Mosquitto 集成 Let‘s Encrypt:deploy 钩子脚本与证书热重载完整指南

Eclipse Mosquitto 集成 Lets Encrypt:deploy 钩子脚本与证书热重载完整指南 【免费下载链接】mosquitto Eclipse Mosquitto - An open source MQTT broker 项目地址: https://gitcode.com/gh_mirrors/mos/mosquitto 导读 Eclipse Mosquitto 在设计上遵循最… · 2026/9/23 20:04:40

ArcGIS Engine 实战:用 TaoToken 统一 Key 打通 GeoDataBase 要素增删与属性读写
ArcGIS Engine 实战:用 TaoToken 统一 Key 打通 GeoDataBase 要素增删与属性读写

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/23 20:04:28

一碗米饭热量与性能优化:3步搞定数据计算痛点
一碗米饭热量与性能优化:3步搞定数据计算痛点

一碗米饭热量与性能优化:3步搞定数据计算痛点 配置环境就卡半天,这种崩溃感谁懂?当你为了跑通一个简单的脚本,折腾了半小时 Docker 镜像,或者在 Python 和 Node… · 2026/9/23 20:04:15

麻雀搜索算法优化SVR回归预测的MATLAB实现与参数调优
麻雀搜索算法优化SVR回归预测的MATLAB实现与参数调优

简介:麻雀搜索算法优化支持向量机回归预测的MATLAB实现,面向需要构建高精度回归模型的研究者与工程师,用于解决SVM参数人工调参困难的问题。资源包共11个文件,包含3个.m主程序与函数、4个mexw64动态库(基于libsvm-3.24… · 2026/9/23 20:04:15

cosmos 项目中的选择排序(Selection Sort):原理、复杂度分析与 9 种语言实现详解
cosmos 项目中的选择排序(Selection Sort):原理、复杂度分析与 9 种语言实现详解

教程示例工程 【免费下载链接】cosmos Worlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project 项目地址: https://gitcode.com/gh_mirrors/co/cosmos 点击查看 免费下载 选择排序(Se… · 2026/9/23 20:04:15

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

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

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码