常见的空指针异常(NullPointerException)及预防方法

前言

请添加图片描述

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:https://www.captainbed.cn/z
在这里插入图片描述
请添加图片描述


1. 错误场景复现

场景1:未校验空值的直接调用

public void processOrder(User user) {
    String address = user.getAddress().toUpperCase(); // 若user或getAddress()返回null则抛出NPE
    System.out.println("配送地址:" + address);
}

初级开发者常假设方法调用链中的每个对象都已初始化,忽略外部传参或中间环节可能返回null的情况。


场景2:自动拆箱引发的NPE

public void calculatePrice(Integer discount) {
    int realDiscount = discount; // 若discount为null,自动拆箱时抛出NPE
    // 业务逻辑...
}

包装类(如Integer)的自动拆箱操作隐藏了空值风险,尤其在接收外部参数时。


场景3:返回null的“陷阱方法”

public List<Order> queryUserOrders(Long userId) {
    // 若用户无订单则直接返回null
    return orderDao.findByUserId(userId); 
}

// 调用方代码
List<Order> orders = queryUserOrders(123L);
for (Order order : orders) { // 当orders为null时抛出NPE
    // 处理订单
}

返回null的方法会强制调用方处理空值,容易遗漏检查。


2. 原理解析

空指针异常的本质

  • 当程序试图在null引用上执行方法调用(如obj.method())或字段访问(如obj.field)时触发
  • 根源:对象生命周期管理失控,未明确约束可为空的变量与不可为空的变量

Java的空值哲学缺陷

  • 默认允许任何对象为null
  • 缺乏编译时强制检查机制(对比Kotlin的Nullable类型标注)

3. 正确解决方案

方案1:拥抱Optional(Java 8+)

// 改造返回null的方法
public Optional<List<Order>> queryUserOrders(Long userId) {
    List<Order> orders = orderDao.findByUserId(userId);
    return Optional.ofNullable(orders); // 包装可能为null的结果
}

// 调用方安全使用
queryUserOrders(123L)
    .orElse(Collections.emptyList()) // 若为null则返回空列表
    .forEach(order -> process(order));
Optional使用规范
  • 不要用Optional作为方法参数(违反设计初衷)
  • 避免Optional.get()直接取值(优先使用orElse()/orElseThrow())

方案2:防御性编程

// 场景1修复:添加空值检查
public void processOrder(User user) {
    if (user == null || user.getAddress() == null) {
        throw new IllegalArgumentException("用户或地址信息不完整");
    }
    String address = user.getAddress().toUpperCase();
    // 或使用Apache Commons工具类
    String address = StringUtils.defaultString(user.getAddress(), "").toUpperCase();
}

// 场景2修复:显式检查包装类
public void calculatePrice(Integer discount) {
    int realDiscount = discount != null ? discount : 0;
}

方案3:永不返回null

// 返回空集合替代null
public List<Order> queryUserOrders(Long userId) {
    List<Order> orders = orderDao.findByUserId(userId);
    return orders != null ? orders : Collections.emptyList(); // 返回不可变空集合
}

// 返回空对象(Null Object模式)
public class EmptyUser extends User {
    public EmptyUser() {
        super(-1L, "未知用户");
    }
}

public User findUserById(Long id) {
    User user = userDao.findById(id);
    return user != null ? user : new EmptyUser();
}

4. 工具与最佳实践

静态代码分析工具

  1. SpotBugs:检测可能抛出NPE的代码路径
  2. SonarQube规则:Null pointers should not be dereferenced
  3. IntelliJ IDEA插件:@NotNull/@Nullable注解支持

注解驱动空安全

// 使用JSR-305注解明确约束
public void updateProfile(@NotNull User user, @Nullable String memo) {
    // 编译器或IDE会提示未做null检查
    System.out.println(user.getName()); 
}

5. Code Review检查清单

检查项正确做法
方法是否可能返回null?优先返回空集合/空对象,或用Optional包装
调用对象方法前是否检查null?链式调用需逐级校验(如user.getAddress().getCity())
是否滥用Optional.isPresent()?用map()/flatMap()替代命令式检查
是否处理了外部输入的空值?对参数做前置校验,使用Objects.requireNonNull()

6. 真实案例

某金融系统在计算用户资产时,未处理用户未开户(account字段为null)的情况:

BigDecimal balance = user.getAccount().getBalance(); // 抛出NPE

后果:线上服务崩溃2小时,影响5万用户查询操作。
修复:

  1. 修改getAccount()方法返回Optional<Account>
  2. 调用方使用user.getAccount().orElse(Account.EMPTY).getBalance()

总结

  • 优先使用Optional:明确表达“可能无值”的语义
  • 防御性编程:对输入参数和中间结果保持警惕
  • 永不返回null:用空集合/空对象消除传染性null
  • 工具辅助:静态分析 + 注解约束 = 编译时防护

下期预告:《集合框架的暗坑:ArrayList遍历时删除元素的灾难》——揭秘ConcurrentModificationException的成因与高并发场景下的终极解决方案。

联系作者

职场经验分享,Java面试,简历修改,求职辅导尽在科技泡泡
思维导图面试视频合集
在这里插入图片描述
在这里插入图片描述

LibTV导演台与General Image Pro:AI真人短剧全流程制作指南 从内容创作走向工程化生产,AI短剧的关键不在于单次生成质量,而在于如何把角色一致性、分镜编排和批量任务变成可复用的工作流。LibTV作为在线SaaS工具,以导演台管理项目层级,通过General Image Pro生成稳定的角色与场景素材,将剧本、分镜、视频生成、配音导出串联成一套流水线。本文从基础概念到实际应用,拆解完整制作流程,覆盖适用边界、批量任务优化、常见故障排查和版权合规红线,帮助内容创作者理解AI短剧的生产逻辑,并快速搭建自己的标准化创作体系。 阅读详情

相关推荐

MATLAB闪退别慌!5分钟搞定Windows系统预设文件修复(附详细截图)

本文针对MATLAB在Windows系统下频繁闪退的问题,提供了快速有效的解决方案。核心在于修复或重置损坏的MATLAB预设文件,通过图文并茂的五步法,指导用户定位AppData目录、重命名预设文件夹,从而让MATLAB自动生成全新配置以解决闪退。文章还涵盖了问题诊断、设置备份及进阶排查方法,帮助用户彻底根除启动故障。

weixin_29234423的博客 928

OPC UA 协议详解

OPC UA(统一架构)是新一代工业自动化标准通信协议,替代传统OPC Classic,具有跨平台、高安全性、标准化等优势。基于TCP协议,支持全平台部署,采用面向对象架构组织数据,提供实时读写、事件订阅、历史数据访问等功能。作为工业4.0核心协议,适用于智能制造、数字孪生等场景,能实现多品牌设备统一接入。相比Modbus和MQTT,OPC UA协议更规范安全但复杂度较高,是大型工业集群互联的主流选择。

eastyuxiao的专栏 732

宝塔面板添加站点及运营商SSL免费证书的申请与使用

本文详细介绍在宝塔面板下如何正确添加站点、配置站点,及在运营商如何申请免费SSL证书,将其配置到宝塔站点里。 给你的网站加一把锁,教你免费SSL证书申请,配置到自己的网站中。 环境:Ubuntu server 18 工具:宝塔控制面板 平台:阿里云 Web服务器:Nginx 宝塔:https://www.bt.cn/ 阿里云:https://www.aliyun.com/ 腾讯云:https://cloud.tencent.com/ 要求: 1.宝塔控制面板搭建完毕。 2掌握域名解析的原理。 3.了解ht

贺兰山长老 5060

5、硬件工程师笔试面试——上拉电阻、下拉电阻(2025版本)

本文系统介绍了上拉电阻和下拉电阻的原理与应用。上拉电阻通过连接电源确保电路高电平状态,常用于数字电路防抖、OC门驱动等场景;下拉电阻连接地线维持低电平,适用于低电平触发电路。文章详细分析了两种电阻的选择方法、功耗与响应速度的平衡策略,以及在高速信号传输中的影响差异。同时对比了二者的优缺点,并提供了模拟电路中的应用实例。最后给出了电阻值计算的工程方法,为硬件工程师的电路设计提供了实用参考。

XU157303764的博客 2873

RK3568笔记十七:LVGL v8.2移植

若该文为原创文章,转载请注明原文出处。本文介绍嵌入式图形库移植到Linux开发板ATK-RK3568上的步骤。

4233

LeRobot 机器人学习(Robot Learning)教程(二)经典机器人学

本文探讨了机器人学中基于学习的方法与传统动力学方法的对比与发展趋势。文章指出,传统方法依赖精确的动力学模型和大量人工调参,面临跨任务泛化能力弱、工程复杂度高、物理建模局限性等问题。随着机器学习技术的进步和机器人数据集的丰富,基于学习的方法展现出三大优势:(1)能够实现跨任务与实体的泛化;(2)降低对人类专业知识的依赖;(3)利用历史数据优化性能。文中以平面机械臂为例,分析了传统逆运动学方法的计算复杂性和环境适应性挑战,并指出基于学习的方案在处理高维感知输入、复杂接触动力学等方面具有明显优势。文章认为,结合物

2506_90492529的博客 1066

《UDS协议从入门到精通》系列——到底什么是DTC?

到底什么是DTC?看这里,深度图解DTC格式,详述DTC状态位、快照信息和扩展信息!!

车载系统攻城狮 1万+

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: String拼接的代价:为何StringBuilder是救星?
下一篇: 集合框架的暗坑:ArrayList遍历时删除元素的灾难
雪碧有白泡泡
雪碧有白泡泡 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域
博客等级 码龄5年 1万+粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪碧有白泡泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值