前四周学了 Lambda、函数式接口、Stream。今天进入第五周第一个主题:Optional。它解决的是 Java 中最常见的 bug 来源——空指针异常。
一、NullPointerException 的根源
Java 中任何引用都可能为 null,每次访问都可能触发 NPE:
User user = findUserById(1);
String city = user.getAddress().getCity(); // user 为 null?address 为 null?
传统做法是层层判空:
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String city = address.getCity();
// ...
}
}
问题:代码被 if (x != null) 淹没,核心逻辑被掩盖。
二、Optional 是什么?
Optional<T> 是一个容器类,要么包含一个非空值,要么什么都不包含。
核心思想:把"可能为空"这件事变成类型系统的一部分,让编译器提醒你处理。
// 返回值用 Optional 包装,调用方必须处理空的情况
Optional<User> user = findUserById(1);
💡 AI 类比:问 AI —— "Java 的 Optional 和前端什么概念类似?"
AI 会告诉你:类似 TypeScript 的
T | null加上 JS 的可选链?.。但 Java 的 Optional 更严格——你必须显式处理空的情况,不能直接访问内部值。
三、创建 Optional 的三种方式
| 方法 | 说明 | 示例 |
|---|---|---|
Optional.of(value) | 值不能为 null,否则抛异常 | Optional.of("Hello") |
Optional.ofNullable(value) | 值可以为 null | Optional.ofNullable(可能为null的变量) |
Optional.empty() | 创建一个空 Optional | Optional.empty() |
Optional<String> nonEmpty = Optional.of("Hello");
Optional<String> maybeNull = Optional.ofNullable(可能为null的值);
Optional<String> empty = Optional.empty();
四、核心方法速览
判断与获取
| 方法 | 作用 |
|---|---|
isPresent() | 是否有值 |
get() | 获取值(为空时抛异常) |
orElse(default) | 有值返回值,无值返回默认值 |
orElseGet(supplier) | 无值时惰性创建默认值 |
orElseThrow(exception) | 无值时抛异常 |
转换与处理
| 方法 | 作用 |
|---|---|
map(function) | 有值时转换,返回新 Optional |
flatMap(function) | 转换方法返回 Optional 时避免嵌套 |
ifPresent(consumer) | 有值时执行 |
filter(predicate) | 满足条件才返回值 |
五、map vs flatMap
map:对 Optional 中的值做转换,返回包装后的新 Optional。
Optional<String> name = Optional.of("java");
Optional<String> upper = name.map(String::toUpperCase);
// Optional[JAVA]
Optional<Integer> length = name.map(String::length);
// Optional[4]
flatMap:当转换方法本身返回 Optional 时,用 flatMap 避免嵌套。
Optional<String> maybeName = Optional.of("java");
// flatMap:返回 Optional<String>
Optional<String> result = maybeName.flatMap(key -> lookup(key));
// Optional[找到: java]
// 如果用 map,会变成嵌套:Optional<Optional<String>>
规则:转换方法返回普通值用
map,返回 Optional 用flatMap。
六、orElse / orElseGet / orElseThrow
Optional<String> empty = Optional.empty();
// orElse:默认值总是会创建
empty.orElse("默认值");
// orElseGet:只在空时才执行 Supplier(惰性)
empty.orElseGet(() -> "延迟创建");
// orElseThrow:空时抛异常
empty.orElseThrow(() -> new RuntimeException("值不存在"));
orElse vs orElseGet 的区别:
Optional<String> filled = Optional.of("存在");
// orElse:即使有值,expensiveOperation 也会执行
filled.orElse(expensiveOperation());
// orElseGet:有值时不会执行
filled.orElseGet(() -> expensiveOperation());
当默认值的创建有开销时,优先用 orElseGet。
七、链式调用:安全获取嵌套属性
// User -> Address -> City
String city = Optional.ofNullable(user)
.flatMap(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
一行代码替代三层 if-null 判断。
八、运行结果
=== 创建 Optional ===
of: Hello
ofNullable(null).isPresent: false
ofNullable('World').isPresent: true
=== map vs flatMap ===
map (转大写): Optional[JAVA]
flatMap (lookup): Optional[找到: java]
=== orElse / orElseGet / orElseThrow ===
empty.orElse('默认'): 默认
empty.orElseGet(() -> '延迟创建'): 延迟创建
orElseThrow: 抛出异常 - 值不存在
=== 链式调用实战 ===
张三 -> 北京
李四 -> 上海
王五 -> 未知城市
张三 -> 朝阳区
李四 -> 上海
王五 -> 未知
九、本节总结
| 场景 | 用哪个方法 |
|---|---|
| 有值转换 | map |
| 转换方法返回 Optional | flatMap |
| 提供默认值 | orElse / orElseGet |
| 空时抛异常 | orElseThrow |
| 有值时执行 | ifPresent |
| 安全链式调用 | flatMap + map + orElse |
十、下篇预告
周四我们将深入学习 Optional 的 filter、ifPresent、流处理等高级用法,以及在博客项目中用 Optional 重构现有的查询逻辑。
系列文章导航:
更多请关注我的微信公众号,感谢!