本文目录导读:

- 基础创建与获取(创建方式)
- 使用 orElse / orElseGet / orElseThrow(核心用法)
- 使用 ifPresent / ifPresentOrElse(消费)
- 使用 map 和 flatMap(转换链式操作)
- 使用 filter(过滤)
- 使用 stream(与 Stream 结合,Java 9+)
- 综合案例:用户地址查询(模拟真实业务)
- 反模式与注意事项(重要!刻意练习避免)
- JDK 版本差异总结
- 核心方法论总结
- 关键原则
Java Optional 是 Java 8 引入的一个容器类,用于优雅地处理可能为空的对象,避免 NullPointerException。
以下是各种场景下的使用案例,从简单到复杂,请认真阅读注释(注:):
基础创建与获取(创建方式)
import java.util.Optional;
public class CreateOptional {
public static void main(String[] args) {
// 1. 创建空的 Optional
Optional<String> emptyOpt = Optional.empty();
// 2. 创建非空的 Optional(传入 null 会抛异常 NPE)
Optional<String> nonEmptyOpt = Optional.of("Hello World");
// 3. 创建允许为 null 的 Optional(推荐)
Optional<String> nullableOpt = Optional.ofNullable(getDataMayBeNull());
System.out.println("emptyOpt 是否存在值: " + emptyOpt.isPresent());
System.out.println("nonEmptyOpt 的值: " + nonEmptyOpt.get());
System.out.println("nullableOpt 的值: " + nullableOpt.orElse("默认值"));
// 注:Optional.of(null) 会直接抛出 NullPointerException
// Optional.of(null); // 异常
}
private static String getDataMayBeNull() {
return null; // 模拟可能返回 null 的情况
}
}
使用 orElse / orElseGet / orElseThrow(核心用法)
import java.util.Optional;
public class OrElseDemo {
public static void main(String[] args) {
String name = null;
// 1. orElse:如果为空,返回默认值(默认值会提前创建)
String result1 = Optional.ofNullable(name)
.orElse("默认值");
System.out.println("orElse结果: " + result1);
// 2. orElseGet:如果为空,通过 Supplier 生成默认值(懒加载)
String result2 = Optional.ofNullable(name)
.orElseGet(() -> "由Supplier生成的默认值");
System.out.println("orElseGet结果: " + result2);
// 3. orElseThrow:如果为空,抛出指定的异常(Java 10+ 可带参数)
try {
Optional.ofNullable(name)
.orElseThrow(() -> new IllegalArgumentException("name 不能为空"));
} catch (Exception e) {
System.out.println("捕获异常: " + e.getMessage());
}
// 4. Java 10 新特性:orElseThrow() 无参,抛出 NoSuchElementException
// Optional.ofNullable(name).orElseThrow();
}
}
使用 ifPresent / ifPresentOrElse(消费)
import java.util.Optional;
public class IfPresentDemo {
public static void main(String[] args) {
String username = "张三";
// 1. ifPresent:值存在时执行动作
Optional.ofNullable(username)
.ifPresent(u -> System.out.println("用户名: " + u));
// 2. ifPresentOrElse(Java 9+):存在时执行动作,否则执行备用动作
Optional.ofNullable(null)
.ifPresentOrElse(
u -> System.out.println("用户名: " + u),
() -> System.out.println("用户名为空,执行备用逻辑")
);
// 3. 传统写法对比(啰嗦且易出错)
if (username != null) {
System.out.println("传统写法用户名: " + username);
}
}
}
使用 map 和 flatMap(转换链式操作)
import java.util.Optional;
public class MapDemo {
public static void main(String[] args) {
Person person = new Person("Alice", new Address("北京", "朝阳区"));
// 1. map:将 Optional 包装的值转换为另一种值
// 场景:获取用户的城市(可能为空)
String city = Optional.ofNullable(person)
.map(Person::getAddress) // 返回 Optional<Address>
.map(Address::getCity) // 返回 Optional<String>
.orElse("未知城市");
System.out.println("城市: " + city);
// 2. flatMap:如果映射结果已经是 Optional,避免嵌套 Optional
// 传统使用 map 会得到 Optional<Optional<String>>
Optional<Optional<String>> nestedOpt = Optional.ofNullable(person)
.map(Person::getAddress)
.map(address -> Optional.ofNullable(address.getStreet()));
// 使用 flatMap 扁平化
String street = Optional.ofNullable(person)
.map(Person::getAddress)
.flatMap(address -> Optional.ofNullable(address.getStreet()))
.orElse("未知街道");
System.out.println("街道: " + street);
// 3. 多个步骤的流水线
String detail = Optional.ofNullable(person)
.map(Person::getAddress)
.map(a -> a.getCity() + "-" + a.getStreet())
.orElse("无地址");
System.out.println("完整地址: " + detail);
// 4. 链式判空(多次转换不会 NPE)
String result = Optional.ofNullable(person)
.map(Person::getAddress) // 若 person 为 null,返回 Optional.empty()
.map(Address::getCity) // 若 address 为 null,返回 Optional.empty()
.map(String::toUpperCase) // 若 city 为 null,返回 Optional.empty()
.orElse("DEFAULT");
System.out.println("链式结果: " + result);
}
}
class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public Address getAddress() {
return address;
}
// getter & setter 省略
}
class Address {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
public String getStreet() {
return street;
}
}
使用 filter(过滤)
import java.util.Optional;
public class FilterDemo {
public static void main(String[] args) {
Integer score = 85;
// 1. 过滤:分数大于 90 才显示
Optional.ofNullable(score)
.filter(s -> s > 90)
.ifPresentOrElse(
s -> System.out.println("优秀学生成绩: " + s),
() -> System.out.println("成绩未达到90分")
);
// 2. 多条件过滤(使用 && 连接)
Optional.ofNullable(score)
.filter(s -> s >= 60 && s <= 100) // 合格范围
.filter(s -> s > 80) // 又是优秀
.ifPresent(s -> System.out.println("优良成绩: " + s));
// 3. 与 map 组合:过滤然后转换
String grade = Optional.ofNullable(score)
.filter(s -> s >= 0 && s <= 100)
.map(s -> s >= 90 ? "A" : s >= 80 ? "B" : s >= 70 ? "C" : "D")
.orElse("无效成绩");
System.out.println("成绩等级: " + grade);
}
}
使用 stream(与 Stream 结合,Java 9+)
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamCombineDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add(null);
names.add("Bob");
names.add("");
// 1. 过滤掉 null 和空字符串
List<String> validNames = names.stream()
.map(Optional::ofNullable) // 包装成 Optional
.flatMap(Optional::stream) // 扁平化流(空 Optional 自动跳过)
.filter(name -> !name.isEmpty())
.collect(Collectors.toList());
System.out.println("有效名称: " + validNames);
// 2. 使用 Optional 简化 null 判断
List<String> cities = new ArrayList<>();
cities.add("北京");
cities.add(null);
cities.add("上海");
cities.stream()
.map(Optional::ofNullable)
.flatMap(Optional::stream)
.forEach(System.out::println);
// 3. 传统方式对比(大量 if 判断)
for (String name : names) {
if (name != null && !name.isEmpty()) {
System.out.println("传统输出: " + name);
}
}
}
}
综合案例:用户地址查询(模拟真实业务)
import java.util.Optional;
public class UserService {
public static void main(String[] args) {
// 模拟 MySQL 查询结果
User user = findUserById(1); // 可能返回 null
// 业务需求:获取用户所在城市,如果没有用户或地址,打印"未知"
String city = Optional.ofNullable(user)
.map(User::getAddress) // 获取地址
.map(Address2::getCity) // 获取城市
.orElse("未知");
System.out.println("用户所在城市: " + city);
// 业务需求:如果是北京用户,打印欢迎语;否则打印其他
Optional.ofNullable(user)
.map(User::getAddress)
.map(Address2::getCity)
.filter(c -> "北京".equals(c))
.ifPresentOrElse(
c -> System.out.println("欢迎来到北京!"),
() -> System.out.println("您好!")
);
// 业务需求:获取用户的第一个亲戚的名字,否则默认"无亲戚"
String relative = Optional.ofNullable(user)
.map(User::getRelative)
.map(Relative::getName)
.orElse("无亲戚");
System.out.println("亲戚: " + relative);
}
private static User findUserById(int id) {
// 模拟数据库返回 null
return new User(new Address2("上海", "浦东"), new Relative("王五"));
}
}
class User {
private Address2 address;
private Relative relative;
public User(Address2 address, Relative relative) {
this.address = address;
this.relative = relative;
}
public Address2 getAddress() {
return address;
}
public Relative getRelative() {
return relative;
}
}
class Address2 {
private String city;
private String street;
public Address2(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
}
class Relative {
private String name;
public Relative(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
反模式与注意事项(重要!刻意练习避免)
import java.util.Optional;
public class AntiPatternsDemo {
public static void main(String[] args) {
String name = null;
// ❌ 反模式1:调用 get() 不判空(会抛 NoSuchElementException)
// String dangerous = Optional.ofNullable(name).get(); // 异常!
// ✅ 正确做法
String safe = Optional.ofNullable(name).orElse("默认");
// ❌ 反模式2:用 Optional 作为方法参数(不推荐,仍可能传 null)
// void setName(Optional<String> name) { ... } // 不推荐
// ✅ 推荐:方法参数直接传 null 即可,方法内部用 Optional 处理
// void setName(String name) {
// this.name = Optional.ofNullable(name).orElse("默认名");
// }
// ❌ 反模式3:滥用 isPresent() + get() 替代传统 if
if (Optional.ofNullable(name).isPresent()) {
// 仍然存在空值风险,不如直接用 if (name != null)
}
// ✅ 正确:使用 ifPresent 或 map + orElse
Optional.ofNullable(name).ifPresent(n -> System.out.println("名字: " + n));
// ❌ 反模式4:将 Optional 用作集合元素/Map键值
// Map<String, Optional<String>> map = ...; // 不建议
// ✅ 正确:Map 值直接用 null 表示不存在
// Map<String, String> map = new HashMap<>();
}
}
JDK 版本差异总结
// JDK 8: // - Optional.empty(), Optional.of(), Optional.ofNullable() // - get(), orElse(), orElseGet(), orElseThrow() // - ifPresent(), map(), flatMap(), filter() // - isPresent() // // JDK 9+ 新增: // - ifPresentOrElse(consumer, runnable) // - or(Supplier<Optional<T>>) // - stream() // // JDK 10+ 新增: // - orElseThrow() 无参版本(更简洁)
核心方法论总结
| 场景 | 推荐写法 |
|---|---|
| 需要一个默认值 | orElse("默认值") 或 orElseGet(() -> ...) |
| 需要抛异常 | orElseThrow(IllegalArgumentException::new) |
| 有值则消费 | ifPresent(...) 或 ifPresentOrElse(...) |
| 链式获取属性 | map(...).map(...).orElse(...) |
| 过滤条件 | filter(...).orElse(...) |
| 与集合结合 | flatMap(Optional::stream) |
关键原则
- 不要对
get()不加保护地调用 —— 极易抛异常。 - 不要用
Optional作为字段、方法参数、集合元素 —— 会破坏代码可读性和性能。 Optional是给返回类型用的,不是为了替代所有 null 判空。- 优先使用
ifPresent、map、orElse,减少显式条件判断。 - Java 9+ 中,可以用
ifPresentOrElse替代传统if-else。 - 谨慎使用
map返回Optional时,考虑flatMap避免嵌套。
通过以上案例,你应该能熟练用 Optional 写出健壮、简洁、无空指针异常的代码!