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

一、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. 实际行为 :当传入的 num1null 时,程序抛出 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);
    }
}
相关推荐
win x7 分钟前
Redis 使用~如何在Java中连接使用redis
java·数据库·redis
星晨雪海14 分钟前
基于 @Resource 的支付 Service 多实现类完整示例
java·开发语言
阿维的博客日记21 分钟前
什么是逃逸分析
java·juc
Ricky_Theseus1 小时前
C++右值引用
java·开发语言·c++
Rick19931 小时前
Java内存参数解析
java·开发语言·jvm
我是大猴子1 小时前
Spring代理类为何依赖注入失效?
java·后端·spring
勿忘,瞬间1 小时前
多线程之进阶修炼
java·开发语言
014-code2 小时前
线程池参数怎么配才不翻车
java
吴梓穆2 小时前
UE5 c++ 常用方法
java·c++·ue5
王夏奇2 小时前
python中的__all__ 具体用法
java·前端·python