看似简单的空指针 —— 包装类自动拆箱陷阱

一、Bug 场景

在一个 Java 应用程序中,涉及到一些基本数据类型与包装类的操作。开发人员在处理业务逻辑时,认为代码逻辑清晰简单,但在特定情况下,程序却抛出了空指针异常(NullPointerException),导致程序崩溃,影响了业务功能的正常运行。

二、代码示例

业务逻辑类(有缺陷)

java 复制代码
public class BusinessLogic {
    public static int calculateSum(Integer num1, Integer num2) {
        // 预期是对两个数求和,忽略了包装类可能为空的情况
        return num1 + num2; 
    }
}

测试代码

java 复制代码
public class NullPointerBugExample {
    public static void main(String[] args) {
        Integer a = null;
        Integer b = 5;
        try {
            int result = BusinessLogic.calculateSum(a, b);
            System.out.println("计算结果: " + result);
        } catch (NullPointerException e) {
            System.out.println("捕获到空指针异常: " + e.getMessage());
        }
    }
}

三、问题描述

  1. 预期行为 :calculateSum 方法应正确计算两个数的和并返回结果,即使其中一个参数为 null,也应按照业务逻辑处理,而不是抛出空指针异常。
  2. 实际行为 :当传入的 num1 为 null 时,程序抛出 NullPointerException。这是因为在 Java 中,当对包装类(如 Integer)进行算术运算时,会发生自动拆箱,即将包装类转换为基本数据类型。在上述代码中,num1 + num2 时,num1 会自动拆箱为 int 类型。然而,null 无法转换为基本数据类型,因此抛出空指针异常。尽管代码表面上看起来只是简单的加法运算,但由于自动拆箱机制,隐藏了潜在的空指针风险。

四、解决方案

  1. 显式空值检查:在进行运算之前,对可能为空的包装类进行显式的空值检查。
java 复制代码
public class BusinessLogic {
    public static int calculateSum(Integer num1, Integer num2) {
        if (num1 == null) {
            num1 = 0;
        }
        if (num2 == null) {
            num2 = 0;
        }
        return num1 + num2; 
    }
}
  1. 使用 Optional 类(Java 8+) :Optional 类提供了一种更优雅的方式来处理可能为空的值。
java 复制代码
import java.util.Optional;

public class BusinessLogic {
    public static int calculateSum(Optional<Integer> num1Opt, Optional<Integer> num2Opt) {
        int num1 = num1Opt.orElse(0);
        int num2 = num2Opt.orElse(0);
        return num1 + num2; 
    }
}

测试代码调整为:

java 复制代码
import java.util.Optional;

public class NullPointerBugExample {
    public static void main(String[] args) {
        Optional<Integer> a = Optional.ofNullable(null);
        Optional<Integer> b = Optional.of(5);
        int result = BusinessLogic.calculateSum(a, b);
        System.out.println("计算结果: " + result);
    }
}
相关推荐
vx-程序开发8 小时前
【计算机毕设】51300童装超市管理系统的设计与实现
java·vue.js·spring boot·源码·课程设计·毕设·大作业
ym hyd 1118 小时前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
risc1234568 小时前
什么是码点?
java
JavaPub-rodert9 小时前
公共 Docker 镜像源总失效?用 Harbor Proxy Cache 自建 Docker Hub 镜像缓存
java·缓存·docker
智鸟科技GemeOpen开发者智能设备9 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
自强的小白9 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务
Wang's Blog9 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生9 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入
她的男孩10 小时前
分片上传的大文件人人可下载:文件模块 isPrivate 在合并时被抹成 false,另有 4 个静默坑
java·后端·架构
SWAGGY..10 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法