聊一聊日常开发中如何优雅的避免那无处不在的空指针异常

在Java编程语言中,NullPointerException(简称NPE)是一种常见的运行时异常,当程序试图访问或操作一个还未初始化(即值为null)的对象引用时,Java虚拟机就会抛出NullPointerException。如果我们在日常开发中,不能很好的去规避NPE,那么可能因为数据或者其他问题就会导致线上问题。。。很烦。。。。

阿里巴巴开发手册规约中也说明防止NPE,是程序员的基本素养。。。
image.png

接下来我们先谈谈几种可能会出现空指针异常的方式。

出现空指针异常的情况

  • 访问空对象的属性或调用空对象的方法
    当一个对象是null时,试图访问一个对象的属性或调用其方法,就会触发空指针异常。
String text = null;
int length = text.length();User user = null;
String userName = user.getUserName();
  • 数组为null或者数组元素为null
    当尝试访问数组中的某个索引处的元素,而该元素为null时,同样会导致空指针异常。
String[] strs = null;
int length = strs.length;String[] strs = new String[3];  
int length = strs[2].length();
  • 集合中null元素访问
    当集合中存在null元素,当我们遍历集合,访问到这个元素的属性或者方法时也会抛出NPE,这种情况也会出现在我们的日常开发中,有时候就会因为数据问题导致这种情况发生,常常也莫名其妙。。。。
List<String> list = Lists.newArrayList();  
list.add(null);  
System.out.println(list.get(0).length());
  • 调用的方法返回null
    调用某个方法,期望其返回一个非null的对象,但实际返回了null。当然这种情况等同于访问空对象的属性或者方法。这在实际开发过程中极易出现的一种情况。比如我们使用Mybatis从数据库中查询一条记录时,数据不存在,就会返回null。这种情况尤为注意。
private User getUserInfo(){  return null;  
}User user = getUserInfo();
String userName = user.getUserName();
  • 使用基本数据类型的包装类
    在使用基本数据类型的包装类时,如果未正确初始化,再转成int时,可能导致空指针异常。
Integer i = null;  
int num = i;

以上大概是我想到或者常遇到的一些可能会发生NPE的情况,如果还有其他情况,可以贴出来讨论。

那么我们该如何避免NPE呢?

避免NPE的几种方式

  • 访问对象前要谨慎
    在使用对象之前,始终检查它是否为null。这包括方法参数、返回值以及对象的属性。在访问对象的方法或属性之前,使用条件语句判断对象是否为null。比如我们在访问User对象前,一定要判null
User user = new User();  
if (user != null){  String userName = user.getUserName();  Address address = user.getAddress();  if (address != null){  String coutry = address.getCountry();  }  
}

或者我们的user是从一个方法中获取的,例如数据库中查询,那么我们在访问这个对象前,一定要判null,如果为null要抛出对应的业务异常,然后我们就可以在接口响应中对应返回错误的信息即可,此时就算是一个正常的流程了。这点尤为重要,一定要注意。

User user = userManager.getUserById(Long userId);
if (user == null){throw new ServiceException(""当前查询的对象不存在);
}

关于SpringBoot项目中捕获自定义业务异常,统一异常管理,统一结果返回,可以参考这篇文章:SpringBoot统一结果返回,统一异常处理,大牛都这么玩 | 码农Academy的博客

当然如果使我们在写User getUserById(Long id)返回对象或者List<User> listUserByIds(List<Long> idList)时我们可以不返回null,可以返回一个对象默认信息或者一个空集合,这样调用方就不会出现NPE风险,当然我们不强制返回一个对象或者空集合,但是必须添加注释充分 说明什么情况下会返回null值。这也是阿里巴巴开发手册规约的建议。
image.png

  • 使用Optional类
    JDK8以上版本提供了Optional类,它是一个容器对象,可用于包装可能为null的值。我们可以使用它判断null问题,同时也解决了多层级访问问题,配合使用orElse时,会先执行orElse方法,然后执行逻辑代码,不管是否出现了空指针。
String country = Optional.ofNullable(user)  .map(User::getAddress)  .map(Address::getCountry)  .orElse("");String country = Optional.ofNullable(user)  .map(User::getAddress)  .map(Address::getCountry)  .orElseGet(() -> defaultContry());private String defaultContry(){return "CN";
}

我们还可以使用orElseThrow()方法,当Optional中的对象是一个null时我们直接抛出异常:

String userName = Optional.ofNullable(user).map(User::getUserName).orElseThrow(() -> new ServiceException("当前用户信息不存在"));
  • 使用断言避免空指针
    使用Java断言(assert)来检查变量是否为null。但要注意,断言通常在开发和测试阶段启用,而在生产环境中可能被禁用(在生产环境中,通常不会启用断言以避免不必要的性能开销以及防止潜在的错误信息泄漏)。
User user = new User();
assert user != null : "user should not be null";
Address address = user.getAddress();  
assert address != null : "address should not be null";  
String coutry = address.getCountry();  
  • 使用@Nullable注解
    使用javax.annotation.Nullable注解,@Nullable注解通常用于标记一个方法的参数、返回值或者字段可能为null。这个注解并非Java标准库的一部分,但在一些第三方库(如JSR 305库中的javax.annotation.Nullable,以及Google Guava和JetBrains的Kotlin标准库等)中广泛使用,并且被许多IDE和静态分析工具支持。以便在编译期或开发工具中提示可能的NPE风险。
@Nullable  
private static User getUserById(Long userId){  return null;  
}  private static void handlerUser(@Nullable User user){  System.out.println(user.getUserName());  
}public static void main(String[] args) {  Long userId = 0L;  User user = getUserById(userId);  String userName = user.getUserName();  handlerUser(user);  
}

此时IDEA就会警告会出现NPE风险
image.png

  • 借助工具扫描代码
    在Java开发中,我们还可以使用以下工具扫描代码以发现潜在的空指针异常风险。
  1. IntelliJ IDEA:内置了强大的静态代码分析器,能够检测出可能的NPE和其他代码问题。
  2. SonarQube / SonarLint:提供持续集成和本地IDE插件形式的静态代码分析,能找出潜在的空指针以及其他质量或安全问题。Sonar可以定时扫描仓库中的代码,可以发现代码中的一些潜在风险,可以通过一些通知例如邮件等告知代码提交者这段代码的风险。
  3. FindBugs(现更名为SpotBugs):另一个开源的静态分析工具,能够发现潜在的bug,包括可能导致NPE的情况。
  4. 阿里巴巴Java开发规约插件: 对于Eclipse和IntelliJ IDEA都有相应的插件版本,基于阿里巴巴内部Java编码规范,包含了对可能出现NPE情况的检测。

补充一点

在JDK 17中引入的Helpful NullPointerExceptions特性确实增强了空指针异常信息的准确性与可用性。当发生NullPointerException时,JVM现在能够提供更精确的位置信息,特别是在链式调用场景下,它会指出导致空指针异常的具体对象引用。这有助于开发者更快地定位到代码中的问题所在,无需通过堆栈跟踪逐层分析来判断哪个对象引用为null。
假如我们访问user.getAddress().getCountry().length()时,在JDK17以前,如果发生了空指针异常,他只会打印出来发生了空指针异常,但是并没有告知到底是user对象还是address对象还是coutnry发生了异常:

Exception in thread "main" java.lang.NullPointerExceptionat com.study.base.core.base.NpeTest.main(NpeTest.java:23)

但是在JDK17以后,借助Helpful NullPointerExceptions特性,异常信息将更加精确,可能会类似打印这样的信息,精确到那个值发生了空指针异常:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Address.getCountry()" because "user.address" is nullat com.study.base.core.base.NpeTest.main(NpeTest.java:23)

这又多了一个升级JDK到17以上的理由。

结论

NullPointerException(NPE)是Java开发中常见的运行时异常,源于对未初始化或已置为null的对象引用进行操作。在实际开发过程中,进行非空检查、使用Optional类以及采用Null安全注解以及使用检查工具等策略可以有效避免此类异常的发生。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/730548.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【技术】基于Github Pages搭建个人博客静态网页

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 文章目录 一、技术基础二、新建特殊仓库三、上传网页文件四、Github Pages设置 个人网页作为仅服务个人的网页&#xff0c;一…

【打工日常】使用docker部署IT运维管理平台CAT

​一、CAT介绍 CAT是一个专为 IT 运维从业者打造的一站式解决方案平台&#xff0c;包含资产管理、工单、工作流、仓储等功能模块。 本项目是celaraze/chemex重构版&#xff0c;原项目chemex名称弃用&#xff1b;CAT采用全新架构设计&#xff0c;大量提升使用体验的细节&#xf…

Trie巧妙解决前后缀问题,3045. 统计前后缀下标对 II

目录 一、题目 1、题目描述 2、接口描述 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 ​Python3 c 一、题目 1、题目描述 给你一个下标从 0 开始的字符串数组 words 。 定义一个 布尔 函数 isPrefixAndSuffix &#xff0c;它接受两个字符串参数 str…

JavaWeb笔记 --- 二、Maven

二、Maven Maven概述 所有的IDE创建的Maven项目都可以使用 Maven简介 Maven模型 Maven常用命令 Maven生命周期 Maven坐标 依赖管理 dpendencies&#xff1a;依赖 依赖范围

java-ssm-jsp-大学生互动交流网站设计与实现

java-ssm-jsp-大学生互动交流网站设计与实现 获取源码——》公主号&#xff1a;计算机专业毕设大全

STM32基础--构建自己的固件库

CMSIS 标准及库层次关系 因为基于 Cortex 系列芯片采用的内核都是相同的&#xff0c;区别主要为核外的片上外设的差异&#xff0c;这些差异却导致软件在同内核&#xff0c;不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的 Cortex 微控制器软件的兼容性问题&#xff0…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:DatePicker)

日期选择器组件&#xff0c;用于根据指定日期范围创建日期滑动选择器。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 DatePicker(options?: DatePickerOptions) 根据指定范…

java及特性的简单介绍

简介&#xff1a; 印度尼西亚有一个盛产咖啡的岛屿java&#xff0c;中文名翻译为爪哇&#xff0c;给这种新语言起码java 寓意端上一杯热咖啡。 特性&#xff1a; 1.面向对象 2.与平台无关 3.稳定安全 4.多线程 面向对象 1.面向对象语言关注的是对象&#xff0c;而不关注过程 2…

Redis缓存穿透-缓存雪崩-缓存击穿

什么叫缓存穿透? 模拟一个场景: 前端用户发送请求获取数据,后端首先会在缓存Redis中查询,如果能查到数据,则直接返回.如果缓存中查不到数据,则要去数据库查询,如果数据库有,将数据保存到Redis缓存中并且返回用户数据.如果数据库没有则返回null; 这个缓存穿透的问题就是这个…

linux安装todesk

xunilToDesk远程桌面软件-免费安全流畅的远程连接电脑手机ToDesk远程控制软件是一款稳定流畅的远程控制电脑手机连接软件,可远程桌面办公,远程协助运维.采用端对端加密,让每一次远程访问都安全可靠。https://www.todesk.com/linux.htmlToDesk远程控制软件是一款稳定流畅的远程控…

【考研数学】李林《880》vs 李永乐《660》完美使用搭配

没有说谁一定好&#xff0c;只有适不适合自身情况&#xff0c;针对自身弱点选择性价比才最高。 两者侧重点不同&#xff0c;660适合强化前期&#xff0c;弥补基础的不足&#xff0c;880适合强化后期&#xff0c;题型全面&#xff0c;提高我们对综合运用知识的能力。 选择习题…

蓝桥杯2023年-买瓜(dfs,类型转换同样耗时)

题目描述 小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜&#xff0c;每个瓜的重量为 Ai 。 小蓝刀功了得&#xff0c;他可以把任何瓜劈成完全等重的两份&#xff0c;不过每个瓜只能劈一刀。 小蓝希望买到的瓜的重量的和恰好为 m 。 请问小蓝至少要劈多少个瓜才能买到重量恰好…

计算机组成原理之机器:存储器之辅助存储器

计算机组成原理之机器&#xff1a;存储器之辅助存储器 笔记来源&#xff1a;哈尔滨工业大学计算机组成原理&#xff08;哈工大刘宏伟&#xff09; Chapter3&#xff1a;存储器之辅助存储器 3.1 概述 3.2 磁记录原理 通不同方向电流时磁化方向不同&#xff0c;由此区分写入…

BUUCTF:[MRCTF2020]ezmisc

题目地址&#xff1a;https://buuoj.cn/challenges#[MRCTF2020]ezmisc 下载附件打开是一张照片&#xff1a; 放到kali中发现crc校验错误&#xff0c;修改照片宽高&#xff1a; 保存即可发现flag flag为&#xff1a; flag{1ts_vEryyyyyy_ez!}

报错:ModuleNotFoundError: No module named ‘tensorrt’

写在前面 我安装了tensorRT,在运行它自带的模型都没问题。 但是在代码中import tensorrt就报错&#xff1a; ModuleNotFoundError: No module named ‘tensorrt’。 网上搜了一大堆&#xff0c;发现是没有在自己的python环境下安装。 所以特意写这篇文章记录一下。 在进行下一…

webUI自动化测试框架

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

C#中实现接口的一些小知识(C#用abstract或virtual来实现接口成员)

文章目录 不可用的修饰可用的修饰非抽象类实现接口抽象类实现接口抽象类与接口方法同名时一同实现 不可用的修饰 在C#中实现接口时&#xff0c;我们不能直接使用static或const来实现接口成员&#xff0c;因为接口中的成员默认都是实例成员&#xff0c;并且它们表示一种契约&am…

c++的STL(2)-- vector容器

目录 1. 默认构造 代码: 相关知识点: 2. 有参构造函数 以及 使用{}初始化对象 代码: 相关知识点: 3. vector容器在尾部添加和删除元素 代码: 使用push_back()和pop_back()进行尾部元素的添加和删除 相关知识点: 代码: 使用emplace_back在尾部添…

人工蜂群算法

人工蜂群算法 人工蜂群算法&#xff08;Artificial Bee Colony Optimization,ABC&#xff09;是一种基于蜜蜂觅食行为的优化算法&#xff0c;由土耳其学者Karaboga于2005年提出&#xff0c;算法模拟蜜蜂的采蜜行为对优化问题进行求解。 算法原理 ABC算法的核心思想是将优化问…

Mysql学习笔记之事务详解(读未提交、读以提交、可重复读、串行化读)

在这个博主的基础上&#xff0c;增加两种情况的对比&#xff1a;https://blog.csdn.net/llllllkkkkkooooo/article/details/108068919 可重复读中幻读现象&#xff08;未使用MVCC&#xff09; 设置可重复读的隔离级别 set global transaction isolation level repeatable read…