1. 概述
在 Java 中将字符串转换为 double
类型时,我们通常会使用 Double.parseDouble(String value)
方法。这个方法可以把一个表示浮点数的字符串(例如 "2.0"
)解析成对应的原始 double
值。
和大多数方法调用一样,传入 null
是危险操作,极有可能在运行时抛出 NullPointerException
。
✅ 本文重点探讨在调用 Double.parseDouble
之前如何安全地检查 null 值。我们会先从纯 Java 实现入手,再介绍几个常用第三方库的解决方案,帮你避免踩坑。
2. 为什么要检查 null?
先来看如果不做检查会怎样。我们分别测试空字符串和 null
输入。
❌ 空字符串导致 NumberFormatException
double emptyString = Double.parseDouble("");
执行结果:
Exception in thread "main" java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
...
❌ null 导致 NullPointerException
double nullString = Double.parseDouble(null);
执行结果:
Exception in thread "main" java.lang.NullPointerException
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1838)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
...
⚠️ 虽然异常处理是正常流程的一部分,但像 NumberFormatException
和 NullPointerException
这类 unchecked 异常,通常是由于编码疏忽导致的。我们应该在调用前主动防御,而不是依赖异常捕获来兜底。
3. 使用核心 Java 实现检查
3.1 原生 Java 判断
最简单直接的方式:手动判断 null
或空字符串。
private static double parseStringToDouble(String value) {
return value == null || value.isEmpty() ? Double.NaN : Double.parseDouble(value);
}
- 如果输入为
null
或空字符串,返回Double.NaN
- 否则正常解析
进一步优化:支持自定义默认值
private static double parseStringToDouble(String value, double defaultValue) {
return value == null || value.isEmpty() ? defaultValue : Double.parseDouble(value);
}
测试用例:
assertThat(parseStringToDouble("1", 2.0d)).isEqualTo(1.0d);
assertThat(parseStringToDouble(null, 1.0d)).isEqualTo(1.0d);
assertThat(parseStringToDouble("", 1.0d)).isEqualTo(1.0d);
✅ 简单粗暴,无需依赖,适合轻量级场景。
3.2 使用 Optional
Java 8+ 推荐使用 Optional
来表达可能缺失的值,语义更清晰。
private static Optional<Double> parseStringToOptionalDouble(String value) {
return value == null || value.isEmpty()
? Optional.empty()
: Optional.of(Double.valueOf(value));
}
调用方式更灵活:
// 判断是否存在
parseStringToOptionalDouble("2").isPresent();
// 获取值或提供默认
parseStringToOptionalDouble("1.0").orElse(2.0d);
parseStringToOptionalDouble(null).orElse(2.0d);
parseStringToOptionalDouble("").orElse(2.0d);
✅ 优势:
- 明确表达“可能无值”的语义
- 链式调用友好
- 避免
null
在业务逻辑中蔓延
⚠️ 注意:返回的是 Optional<Double>
而不是 double
,需根据调用方是否需要原始类型做取舍。
4. 第三方库方案
4.1 Google Guava
Maven 依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
使用 Doubles.tryParse
:
Doubles.tryParse(MoreObjects.firstNonNull("1.0", "2.0")); // 返回 1.0
Doubles.tryParse(MoreObjects.firstNonNull(null, "2.0")); // 返回 2.0
⚠️ 注意坑点:Doubles.tryParse("")
返回 null
,而不是抛异常。虽然避免了 NumberFormatException
,但你仍需处理 null
—— 等于把问题往后推了。
Doubles.tryParse(""); // 返回 null
✅ 适合配合 MoreObjects.firstNonNull
做 fallback,但对空字符串不敏感。
4.2 Apache Commons Lang - NumberUtils
Maven 依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
NumberUtils.toDouble
提供两种重载:
NumberUtils.toDouble("1.0"); // 失败返回 0.0d
NumberUtils.toDouble("1.0", 1.0d); // 失败返回指定默认值
处理边界情况:
assertThat(NumberUtils.toDouble("")).isEqualTo(0.0d);
assertThat(NumberUtils.toDouble(null)).isEqualTo(0.0d);
✅ 最大优点:**永远返回 double
原始类型,调用方无需担心 null
**,适合需要“静默失败 + 默认值”的场景。
4.3 Vavr - 函数式 Try
Maven 依赖:
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.10.2</version>
</dependency>
使用 Vavr 的 Try
模拟函数式异常处理:
public static double tryStringToDouble(String value, double defaultValue) {
return Try.of(() -> Double.parseDouble(value)).getOrElse(defaultValue);
}
测试:
assertThat(tryStringToDouble("1", 2.0d)).isEqualTo(1.0d);
assertThat(tryStringToDouble(null, 2.0d)).isEqualTo(2.0d);
assertThat(tryStringToDouble("", 2.0d)).isEqualTo(2.0d);
✅ 优势:
- 函数式风格,异常被封装为值
Try
支持 map/flatMap 等操作,便于链式处理- 与
Optional
类似但更强调“可能失败的计算”
⚠️ 缺点:引入函数式库可能“杀鸡用牛刀”,适合已使用 Vavr 的项目。
5. 总结
方案 | 返回类型 | 是否处理 null/empty | 推荐场景 |
---|---|---|---|
原生 Java | double |
✅ 手动判断 | 无依赖,简单直接 |
Optional |
Optional<Double> |
✅ | Java 8+,强调语义清晰 |
Guava Doubles.tryParse |
Double |
⚠️ 不处理空字符串 | 已用 Guava,且输入可控 |
Commons Lang NumberUtils |
double |
✅ | 需要默认值,避免 null |
Vavr Try |
double |
✅ | 函数式风格项目 |
✅ 推荐优先级:
- 项目已引入
commons-lang3
→ 直接用NumberUtils.toDouble
- 否则 → 自定义方法 + 默认值 或 使用
Optional
- 函数式项目 → Vavr
Try
所有示例代码已上传至 GitHub:https://github.com/example/java-number-parsing