JAVA重点基础、进阶知识及易错点总结(27)Optional 类
·
🚀 Java 巩固进阶 · 第 27 天
主题:Optional 类 —— 优雅解决空指针异常(NPE)
📅 进度概览:继 Lambda 和 Stream 之后,今天学习 JDK8 另一个重要特性:Optional 类。它是 Java 对"null 值处理"的函数式封装,旨在减少恼人的
NullPointerException。💡 核心价值:
- 代码健壮:强制开发者显式处理空值情况,减少线上 NPE 故障。
- 语义清晰:方法返回
Optional<T>明确告知调用者"结果可能为空"。- 链式调用:配合 Stream 和 Lambda,实现流畅的空值处理逻辑。
- 框架集成:Spring Data JPA 的
findById返回Optional,必须掌握。
一、为什么需要 Optional?告别"十亿美元错误" 🛡️
1. 传统 null 处理的痛点
// ❌ 传统写法:层层嵌套 if,代码缩进灾难
public String getCity(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
City city = address.getCity();
if (city != null) {
return city.getName();
}
}
}
return "Unknown";
}
2. Optional 的核心思想
┌─────────────────────────────────────┐
│ 🔄 Optional 本质 │
│ 是一个容器对象,可能包含也可能不包含非空值│
│ │
│ • 不是用来替代所有 null 的 │
│ • 主要用于方法返回值 │
│ • 强制调用者处理空值情况 │
└─────────────────────────────────────┘
💡 一句话理解:
“Optional 是一个盒子,里面可能有值,也可能空着。你要先检查盒子再拿东西,或者直接告诉我不值钱时给什么替代品”
二、核心 API 实战:创建与获取 🛠️
1. 创建 Optional 的 3 种方式
// 1️⃣ of():值不能为 null,否则抛 NPE
Optional<String> opt1 = Optional.of("Hello");
// Optional.of(null); // ❌ 报错!
// 2️⃣ ofNullable():值可以为 null(⭐ 最常用)
String str = null;
Optional<String> opt2 = Optional.ofNullable(str); // ✅ 安全
// 3️⃣ empty():创建一个空的 Optional
Optional<String> opt3 = Optional.empty();
2. 获取值的 4 种方式(⭐ 必背)
Optional<String> opt = Optional.ofNullable(getValue());
// 1️⃣ get():直接获取,为空抛异常(⚠️ 不推荐单独使用)
// String value = opt.get();
// 2️⃣ orElse(default):为空则返回默认值
String value1 = opt.orElse("Default Value");
// 3️⃣ orElseGet(supplier):为空则执行 lambda 生成默认值(性能更好)
String value2 = opt.orElseGet(() -> "Generated: " + System.currentTimeMillis());
// 4️⃣ orElseThrow():为空则抛异常
String value3 = opt.orElseThrow(() -> new RuntimeException("Value not found"));
3. 消费值:ifPresent
// ✅ 推荐:有值则执行,无值则跳过
opt.ifPresent(value -> System.out.println("值:" + value));
// ✅ 进阶:有值执行 A,无值执行 B
opt.ifPresentOrElse(
value -> System.out.println("值:" + value),
() -> System.out.println("值为空")
);
三、进阶用法:链式调用与嵌套对象 🔗
1. 嵌套对象安全访问(核心场景)
// ✅ Optional 链式调用:替代层层 if
public String getCityName(Optional<User> userOpt) {
return userOpt
.map(User::getAddress) // User -> Optional<Address>
.map(Address::getCity) // Address -> Optional<City>
.map(City::getName) // City -> Optional<String>
.orElse("Unknown City"); // 最终默认值
}
⚠️ 关键细节:
map():返回值包装进 OptionalflatMap():返回值已经是 Optional,避免嵌套Optional<Optional<T>>
2. 过滤与转换
Optional<Integer> result = Optional.ofNullable(getNumber())
.filter(n -> n > 10) // 过滤:不大于 10 变空
.map(n -> n * 2); // 转换:乘 2
3. 与 Stream 结合
List<String> names = users.stream()
.map(User::getName) // 可能返回 null
.map(Optional::ofNullable) // 转 Optional
.flatMap(Optional::stream) // 过滤掉空的(JDK9+)
.collect(Collectors.toList());
四、⚠️ 使用禁忌与最佳实践
1. 不要这样做 ❌
// ❌ 1. 不要用 Optional 包装基本类型
Optional<int> opt = Optional.of(1); // 编译报错!
// ✅ 用 OptionalInt, OptionalLong, OptionalDouble
// ❌ 2. 不要把 Optional 用作字段或方法参数
public class User {
private Optional<String> name; // ❌ 不推荐,增加序列化复杂度
}
public void save(Optional<User> user) {} // ❌ 调用方负担重
// ❌ 3. 不要直接调用 get() 而不检查
String val = opt.get(); // 可能抛 NoSuchElementException
2. 推荐这样做 ✅
// ✅ 1. 仅用于方法返回值
public Optional<User> findById(Long id) { ... }
// ✅ 2. 优先使用 orElse/orElseGet
String name = userOpt.map(User::getName).orElse("Anonymous");
// ✅ 3. 链式调用保持流畅
Optional.ofNullable(order)
.map(Order::getPayment)
.ifPresent(payment -> process(payment));
五、🎯 今日实战任务:空值处理重构
任务 1:基础 Optional 使用
/**
* 要求:
* 1. 编写方法 getValue(boolean hasValue) 返回 Optional<String>
* 2. 调用方使用 orElse 获取默认值
* 3. 调用方使用 ifPresent 打印值
*
* 💡 提示:
* return hasValue ? Optional.of("Data") : Optional.empty();
*/
任务 2:嵌套对象安全访问
/**
* 业务场景:获取用户所在城市的邮编
* 对象链:User -> Address -> City -> ZipCode
*
* 要求:
* 1. 创建 User, Address, City 类(关联关系)
* 2. 编写传统写法(多层 if null 检查)
* 3. 编写 Optional 写法(map 链式调用)
* 4. 对比代码行数与可读性
*
* 💡 挑战:
* - 如果中间某层为空,返回 "000000"
*/
任务 3:Optional 与 Stream 结合
/**
* 业务场景:过滤列表中的空值并转换
*
* 要求:
* 1. 创建 List<String> 包含 null 值
* 2. 用 Stream + Optional 过滤掉 null
* 3. 将所有非空字符串转大写并收集
*
* 💡 关键 API:
* .filter(Objects::nonNull) 或 .flatMap(Optional::stream)
*/
任务 4:Spring Data JPA 模拟
/**
* 要求:
* 1. 模拟 Repository findById 方法,返回 Optional<User>
* 2. Service 层调用该方法
* 3. 如果用户不存在,抛 BusinessException
* 4. 如果存在,返回用户姓名
*
* 💡 提示:
* userOpt.orElseThrow(() -> new BusinessException("用户不存在"))
*/
📝 第 27 天 · 核心总结(极简背诵版)
-
创建方式:
Optional.of(value) // 值非空 Optional.ofNullable(value)// 值可空(⭐ 常用) Optional.empty() // 空对象 -
获取值:
opt.orElse(default) // 默认值 opt.orElseGet(lambda) // 延迟生成默认值 opt.orElseThrow(ex) // 空则抛异常 opt.ifPresent(consumer) // 有值则消费 -
链式调用:
opt.map(...).map(...).orElse(...) // 注意:返回值是 Optional 时用 flatMap -
使用禁忌:
- ❌ 不做字段/参数
- ❌ 不包装基本类型(用 OptionalInt 等)
- ❌ 不直接 get()
明天预告:🔧 接口默认方法与静态方法 —— 接口进化论!
- 为什么接口需要 default 方法?(兼容旧实现类)
- 默认方法与抽象类方法的冲突解决
- 接口静态方法的调用方式
- 实战:定义可演进的计算接口
准备好了吗?明天我们完善接口的"进化能力"! 🧬✨
更多推荐




所有评论(0)