当前位置: 首页 > backend >正文

NullPointerException 空指针异常,为什么老是遇到?

网罗开发(小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 摘要
    • 引言
    • 问题分析
    • 常见解决方案
      • 1. 手动判空(最朴素的方法)
      • 2. 使用 `Optional`(推荐写法)
      • 3. 使用 Lombok 的 `@NonNull` 注解(开发时防御)
    • 实际开发场景举例
      • 场景 1:数据库查询结果为空
      • 场景 2:第三方接口返回字段缺失
      • 场景 3:集合取值前检查
    • QA 环节
    • 总结

摘要

在 Java 开发中,NullPointerException(空指针异常) 是最常见也是最让人头疼的异常之一。它通常出现在你操作了一个还没有初始化的对象时。很多同学写项目,尤其是初学时,总会碰到 “空指针” 的报错,明明逻辑没什么问题,结果程序直接崩掉。今天我们就用一个简单的例子,聊聊空指针出现的原因、常见的解决方法,并写个小 Demo 帮助你更好地理解。

引言

Java 的对象在使用之前必须先实例化,这跟 Python、JavaScript 等动态语言有点不一样。在动态语言中,即使你没有定义变量也可能会被“兜底”处理掉,但在 Java 里,一旦你直接对 null 调用方法,就会触发 NullPointerException
所以空指针本质上就是:你把不存在的东西,当作存在的对象来用

问题分析

来看下面这段代码:

public class NullPointerDemo {public static void main(String[] args) {String s = null;System.out.println(s.length());}
}

运行结果:

Exception in thread "main" java.lang.NullPointerExceptionat NullPointerDemo.main(NullPointerDemo.java:4)

这里 snull,却调用了 s.length(),于是程序直接抛出了空指针异常。

很多新手在实际开发中,可能是在处理请求参数、数据库返回值、或者第三方接口返回对象时,忘记做空值检查,就导致了这种问题。比如:

  • 数据库没查到数据返回 null
  • HTTP 接口没返回期望字段;
  • 集合里没有元素直接调用 .get(0)
  • Spring 注入失败,Bean 没有实例化。

常见解决方案

1. 手动判空(最朴素的方法)

public class NullPointerCheck {public static void main(String[] args) {String s = null;if (s != null) {System.out.println(s.length());} else {System.out.println("字符串为空,无法计算长度");}}
}

解释:
这是最常见的防御性写法。在调用对象的方法之前先做空值判断,可以避免异常直接崩掉程序。

2. 使用 Optional(推荐写法)

Java 8 引入了 Optional,它的核心思想是:把可能为空的对象用一个容器包起来,强制你用显式的方式处理空值

import java.util.Optional;public class NullPointerOptional {public static void main(String[] args) {String s = null;// 用 Optional 包裹String result = Optional.ofNullable(s).map(String::length)   // 如果不为 null,就计算长度.map(Object::toString) // 转成字符串.orElse("默认值");      // 如果为 null,返回默认值System.out.println(result);}
}

输出:

默认值

解释:
相比传统 if (obj != null)Optional 写法更优雅,更函数式,也能让代码逻辑更清晰。

3. 使用 Lombok 的 @NonNull 注解(开发时防御)

Lombok 提供了一个 @NonNull 注解,可以在方法参数或成员变量上标记。被标记的参数如果传入了 null,在运行时会自动抛出 NullPointerException,帮助你更快定位问题。

import lombok.NonNull;public class NullPointerLombok {public static void printLength(@NonNull String s) {System.out.println(s.length());}public static void main(String[] args) {String value = null;printLength(value); // 这里会直接抛 NullPointerException}
}

输出:

Exception in thread "main" java.lang.NullPointerException: s is marked non-null but is null

解释:
这不是帮你避免空指针,而是 在第一时间发现空指针,防止错误逻辑蔓延到更深层。

实际开发场景举例

场景 1:数据库查询结果为空

User user = userRepository.findById(123);
if (user != null) {System.out.println(user.getName());
} else {System.out.println("用户不存在");
}

场景 2:第三方接口返回字段缺失

ApiResponse response = apiClient.getUserInfo();
Optional.ofNullable(response.getData()).map(UserData::getNickname).ifPresentOrElse(nickname -> System.out.println("昵称:" + nickname),() -> System.out.println("接口返回数据为空"));

场景 3:集合取值前检查

List<String> list = new ArrayList<>();
if (!list.isEmpty()) {System.out.println(list.get(0));
}

QA 环节

Q:为什么有时候明明加了 @Autowired,还是报空指针?
A:这通常是因为 Spring 的 Bean 没有被扫描到,或者你在非 Spring 管理的类里使用了 Bean。解决办法是检查包扫描路径,或者用 @Component / @Service 标注类。

Q:Optional 会不会影响性能?
A:Optional 本质上是个对象包装,开销很小,一般业务代码中性能损耗可以忽略不计。

总结

  • 空指针异常本质上就是你在操作 null 对象
  • 最简单的方法是 手动判空
  • 推荐使用 Optional 让代码更简洁。
  • 可以用 Lombok 的 @NonNull 提前发现空值问题。
  • 在实际项目中,不同场景需要结合判空逻辑,养成良好的防御性编程习惯。
http://www.xdnf.cn/news/18979.html

相关文章:

  • 评价指标FID/R Precision
  • vscode编辑器中设置断点,不会自动启动调试器
  • 介绍⼀下Llama的结构
  • Spring Boot 整合 MongoDB:CRUD 与聚合查询实战
  • Jenkins 全方位指南:安装、配置、部署与实战应用(含图解)
  • 如何规划一年、三年、五年的IP发展路线图?
  • 01.<<基础入门:了解网络的基本概念>>
  • Leetcode 深度优先搜索 (15)
  • WINTRUST!_ExplodeMessag函数中的pCatAdd
  • Yolov8 pose 推理部署笔记
  • Vue开发避坑:箭头函数与普通函数的正确使用指南
  • LeetCode 刷题【55. 跳跃游戏】
  • 从协作机器人到智能协作机器人:工业革命的下一跳
  • 【JavaScript】递归的问题以及优化方法
  • 安宝特方案丨安宝特工业AR全链路解决方案
  • Unity游戏打包——iOS打包基础、上传
  • java后端的各种注解
  • Linux 禁止 su 的几种限制手段:从 NoNewPrivileges 到 PAM 配置
  • GitHub 宕机自救指南:确保开发工作不间断
  • 大数据毕业设计选题推荐-基于大数据的存量房网上签约月统计信息可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • 学习嵌入式之驱动——I2C子系统
  • 深度学习篇---VGGNet
  • 一个基于物理信息神经网络(Physics-Informed Neural Network, PINN)的多变量时间序列预测模型MATLAB代码
  • Windows 7-11通用,这工具让电脑提速300%
  • 2025.8.28总结
  • HTTP 范围请求:为什么你的下载可以“断点续传”?
  • Chrome 插件开发实战:从入门到精通
  • vue2使用el-form动态参数展示并非空校验
  • 数据结构青铜到王者第九话---二叉树(2)
  • 自下而上的树形dp