Optional 是什么?为什么能消灭 NullPointerException(AI 辅助学习 Java 8)

22 阅读3分钟

前四周学了 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)值可以为 nullOptional.ofNullable(可能为null的变量)
Optional.empty()创建一个空 OptionalOptional.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
转换方法返回 OptionalflatMap
提供默认值orElse / orElseGet
空时抛异常orElseThrow
有值时执行ifPresent
安全链式调用flatMap + map + orElse

十、下篇预告

周四我们将深入学习 Optional 的 filter、ifPresent、流处理等高级用法,以及在博客项目中用 Optional 重构现有的查询逻辑。


系列文章导航

更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg