Java null 与 Optional
Java 引用可空主模型与 Optional 的正确用法:何时用 null、何时用 Optional、如何降低 NPE。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
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 未命中
- 外部 API 失败用 null(坏风格)
2. NPE 典型路径
- 对 null 调方法/访字段
- 拆箱
Integer为int时为 null - 自动拆箱与链式调用
3. Optional 适用
| 适合 | 不适合 |
|---|---|
| 返回值可能缺失 | 方法参数(通常) |
| 明确替代 null 返回 | 实体字段到处 Optional |
| 链式变换 | 当集合用(别 Optional 嵌套爆炸) |
4. 防御技术
Objects.requireNonNull- 早期校验
- 空对象模式(有限度)
- 注解/静态分析(工程篇)
边界情况与反直觉行为
Optional.of(null)立即 NPE;用ofNullable。orElse(getDefault())会无条件执行默认工厂——应用orElseGet。- 序列化与 Optional 字段兼容性视技术栈而定。
常见误区
[!warning] 常见误区:
return null表示错误 错误应用异常或 Result 类型语义;null 表示“无值”,不是“失败原因”。
工程实践
- 公共 API 返回缺失用 Optional 或明确空集合(不要返回 null 集合)。
- 集合返回
List.of()/Collections.emptyList()优于 null。 - 日志与指标记录 NPE 发生点,修根因而非全局 catch。
- 与 Kotlin 互操作时注意平台类型。
本节总结
- null 是主模型;Optional 是返回值工具
- 显式处理缺失;避免伪装成功
- 空集合优于 null 集合
自测题
- 何时用
orElseGet而不是orElse? - 为什么不建议
Optional作所有字段类型?
参考答案
- 默认值创建有开销或副作用时延迟执行。
- 增加分配与嵌套复杂度,领域模型更宜不可变与明确缺省。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Optional (Java SE) | API | Optional 语义 |
| JLS 关于引用类型与 null | 规范 | null 类型 |