Java null 与 Optional
Java 引用可空主模型与 Optional 的正确用法:何时用 null、何时用 Optional、如何降低 NPE。
#concept
#growing
#dev
#java
[!info] related notes
Java null 与 Optional
这个概念为什么出现
引用类型变量可以是 null,表示“没有对象”。这是 Java 历史主模型,也是 NullPointerException 之源。Optional 在标准库层表达“可能缺失的返回值”,但不能消灭语言级 null。
[!abstract] 一句话理解
null是引用的缺省空值;Optional<T>用于明确“可能无结果”的返回场景,应用 map/flatMap/orElse 显式处理,而不是替代所有字段。
最小可运行示例
场景:用户中心按 ID 查找,找不到时不能假装有用户
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class UserLookup {
private final Map<String, String> db = new HashMap<>();
UserLookup() {
db.put("u1", "Ada");
}
// 返回 Optional:显式表达缺失;编译器并不强制安全解包
Optional<String> findName(String id) {
return Optional.ofNullable(db.get(id));
}
static String display(Optional<String> name) {
return name.map(n -> "hi " + n).orElse("hi guest");
}
public static void main(String[] args) {
UserLookup ul = new UserLookup();
System.out.println(display(ul.findName("u1")));
System.out.println(display(ul.findName("missing")));
// 错误示范:Optional 解包前不检查
// ul.findName("missing").get(); // NoSuchElementException
}
}
建议运行:
javac UserLookup.java && java UserLookup
期望输出:
hi Ada
hi guest
结合场景再看三个关注点
- 找不到用空 Optional,而不是 null 返回又文档不写。
get()是尖刀,优先orElse/orElseThrow。- 字段仍可用 null 或明确缺省对象,不要把 Optional 塞满领域模型。
核心概念与准确模型
1. null 出现位置
- 引用类型字段及引用数组元素默认 null;局部变量没有自动可读的默认值,使用前必须满足明确赋值规则。\
- Map.get 未命中可返回 null;允许 null 值的 Map 还需 containsKey 区分“缺键”和“值为 null”。其他容器的缺失行为按 API 判断。\
- 外部 API 失败用 null(坏风格)
2. NPE 典型路径
- 对 null 调方法/访字段
- 拆箱
Integer为int时为 null - 自动拆箱与链式调用
3. Optional 适用
| 适合 | 不适合 |
|---|---|
| 返回值可能缺失 | 方法参数(通常) |
| 明确替代 null 返回 | 实体字段到处 Optional |
| 链式变换 | 当集合用(别 Optional 嵌套爆炸) |
4. 防御技术
Objects.requireNonNull- 早期校验
- 空对象模式(有限度)
- 注解/静态分析(工程篇)
边界情况与反直觉行为
Optional.of(null)立即 NPE;用ofNullable。- orElse(getDefault()) 在调用前就求值,即使 Optional 非空也会执行工厂;需要惰性默认值时用 orElseGet。\
- Optional.map 的映射结果为 null 时变成 empty;flatMap 的映射函数必须返回非 null Optional,否则抛 NPE。
- Optional 本身仍是可空引用,返回 Optional 的 API 应返回 empty 而非 null。
- Optional 是 value-based 类,不依赖实例身份、empty 的单例性或把实例作为锁;序列化支持依技术栈而定。\
常见误区
[!warning] 常见误区:
return null表示错误 错误应用异常或 Result 类型语义;null 表示“无值”,不是“失败原因”。
工程实践
- 公共 API 返回缺失用 Optional 或明确空集合(不要返回 null 集合)。
- 集合返回
List.of()/Collections.emptyList()优于 null。 - 日志与指标记录 NPE 发生点,修根因而非全局 catch。
- 与 Kotlin 互操作时注意平台类型。
本节总结
- null 是主模型;Optional 是返回值工具
- 显式处理缺失;避免伪装成功
- 空集合优于 null 集合
自测题
- 何时用
orElseGet而不是orElse? - 为什么不建议
Optional作所有字段类型?
参考答案
- 默认值创建有开销或副作用时延迟执行。
- Optional 主要面向可能缺失的返回值;遍布字段会增加嵌套与框架映射负担。不可变性本身不消除缺失,仍需明确建模。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Optional (Java SE) | API | Optional 语义 |
| JLS 关于引用类型与 null | 规范 | null 类型 |