本文目录导读:

我来为您详细讲解Java 8的Optional类,包括概念、核心用法、实战案例和最佳实践。
Optional概述
Optional是一个容器对象,用于包含可能为空的值,它提供了一种更优雅的方式来处理空值,避免NullPointerException。
基础使用案例
1 创建Optional对象
import java.util.Optional;
public class CreateOptionalExample {
public static void main(String[] args) {
// 1. 创建包含值的Optional
Optional<String> name = Optional.of("张三");
System.out.println("name: " + name); // Optional[张三]
// 2. 创建空的Optional
Optional<String> empty = Optional.empty();
System.out.println("empty: " + empty); // Optional.empty
// 3. 创建可能为空的Optional
String nullable = null;
Optional<String> optional = Optional.ofNullable(nullable);
System.out.println("optional: " + optional); // Optional.empty
// 注意:Optional.of()不能传null
try {
Optional.of(null);
} catch (NullPointerException e) {
System.out.println("Optional.of(null) 抛出 NullPointerException");
}
}
}
2 检查值是否存在
import java.util.Optional;
public class CheckValueExample {
public static void main(String[] args) {
Optional<String> name = Optional.of("李四");
Optional<String> empty = Optional.empty();
// isPresent() - 检查是否有值
System.out.println("name.isPresent(): " + name.isPresent()); // true
System.out.println("empty.isPresent(): " + empty.isPresent()); // false
// isEmpty() - Java 11+ 检查是否为空
// System.out.println("empty.isEmpty(): " + empty.isEmpty()); // true
// ifPresent() - 如果有值则执行操作
name.ifPresent(value -> System.out.println("值存在: " + value));
empty.ifPresent(value -> System.out.println("永远不会执行"));
}
}
获取值的案例
1 各种获取值的方式
import java.util.NoSuchElementException;
import java.util.Optional;
public class GetValueExample {
public static void main(String[] args) {
Optional<String> name = Optional.of("王五");
Optional<String> empty = Optional.empty();
// 1. get() - 如果值不存在会抛异常(不推荐使用)
System.out.println("get(): " + name.get()); // 王五
try {
empty.get();
} catch (NoSuchElementException e) {
System.out.println("empty.get() 抛出 NoSuchElementException");
}
// 2. orElse() - 如果值不存在返回默认值(推荐)
System.out.println("orElse: " + empty.orElse("默认值")); // 默认值
// 3. orElseGet() - 如果值不存在,通过Supplier提供默认值
System.out.println("orElseGet: " + empty.orElseGet(() -> "懒加载默认值"));
// 4. orElseThrow() - 如果值不存在抛出指定异常
try {
empty.orElseThrow(() -> new IllegalArgumentException("值不存在"));
} catch (IllegalArgumentException e) {
System.out.println("抛出自定义异常: " + e.getMessage());
}
// 5. orElseThrow() 无参数版本(Java 10+)
try {
empty.orElseThrow();
} catch (NoSuchElementException e) {
System.out.println("抛出默认 NoSuchElementException");
}
}
}
实战案例
1 用户信息查询系统
import java.util.Optional;
public class UserService {
// 用户数据模拟
private static final User[] USERS = {
new User(1L, "张三", "zhangsan@example.com", "北京"),
new User(2L, "李四", null, "上海"),
new User(3L, "王五", "wangwu@example.com", null)
};
// 查询用户(传统方式 vs Optional方式)
public static Optional<User> findUserById(Long id) {
for (User user : USERS) {
if (user.getId().equals(id)) {
return Optional.ofNullable(user);
}
}
return Optional.empty();
}
// 获取用户邮箱(安全获取,不抛异常)
public String getUserEmail(Long userId) {
return findUserById(userId)
.map(User::getEmail)
.orElse("无邮箱信息");
}
// 获取用户城市(链式操作)
public String getUserCity(Long userId) {
return findUserById(userId)
.map(User::getCity)
.filter(city -> city.length() > 0)
.orElse("未知城市");
}
// 复杂业务:发送邮件
public void sendEmailToUser(Long userId) {
findUserById(userId)
.filter(user -> user.getEmail() != null)
.map(User::getEmail)
.ifPresentOrElse(
email -> System.out.println("发送邮件到: " + email),
() -> System.out.println("用户无邮箱,无法发送")
);
}
// 用户类
static class User {
private Long id;
private String name;
private String email;
private String city;
public User(Long id, String name, String email, String city) {
this.id = id;
this.name = name;
this.email = email;
this.city = city;
}
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public String getCity() { return city; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "'}";
}
}
public static void main(String[] args) {
UserService service = new UserService();
// 测试各种场景
System.out.println("=== 用户邮箱查询 ===");
System.out.println("用户1邮箱: " + service.getUserEmail(1L)); // 有邮箱
System.out.println("用户2邮箱: " + service.getUserEmail(2L)); // 无邮箱
System.out.println("用户99邮箱: " + service.getUserEmail(99L)); // 不存在
System.out.println("\n=== 用户城市查询 ===");
System.out.println("用户1城市: " + service.getUserCity(1L));
System.out.println("用户3城市: " + service.getUserCity(3L)); // 城市为null
System.out.println("用户99城市: " + service.getUserCity(99L)); // 用户不存在
System.out.println("\n=== 发送邮件测试 ===");
service.sendEmailToUser(1L); // 有邮箱
service.sendEmailToUser(2L); // 无邮箱
service.sendEmailToUser(99L); // 用户不存在
}
}
2 配置管理系统
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class ConfigManager {
private static final Map<String, String> CONFIG = new HashMap<>();
static {
CONFIG.put("app.name", "MyApp");
CONFIG.put("app.version", "1.0.0");
CONFIG.put("app.port", "8080");
}
// 获取配置项
public Optional<String> getConfig(String key) {
return Optional.ofNullable(CONFIG.get(key));
}
// 获取数值类型配置
public Optional<Integer> getIntegerConfig(String key) {
return getConfig(key)
.map(value -> {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return null;
}
});
}
// 获取端口(带默认值)
public int getPort() {
return getIntegerConfig("app.port")
.filter(port -> port > 0 && port < 65536)
.orElse(8080);
}
public static void main(String[] args) {
ConfigManager config = new ConfigManager();
System.out.println("应用名称: " + config.getConfig("app.name").orElse("未设置"));
System.out.println("应用端口: " + config.getPort());
System.out.println("最大连接数: " +
config.getIntegerConfig("max.connections").orElse(100));
// 链式配置读取
String databaseUrl = config.getConfig("database.url")
.orElseGet(() -> "jdbc:mysql://localhost:3306/default");
System.out.println("数据库URL: " + databaseUrl);
}
}
3 复杂的流式处理
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class OptionalStreamExample {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("张三", 8000, "技术部"),
new Employee("李四", 6000, "市场部"),
new Employee("王五", null, "技术部"),
new Employee("赵六", 15000, "管理部"),
new Employee("钱七", 12000, null)
);
// 1. 过滤出有薪水的员工,并计算总薪水
double totalSalary = employees.stream()
.map(Employee::getSalary)
.filter(Optional::isPresent)
.mapToDouble(Optional::get)
.sum();
System.out.println("总薪水: " + totalSalary);
// 2. 使用flatMap处理Optional
double totalSalary2 = employees.stream()
.map(Employee::getSalary)
.flatMap(Optional::stream)
.mapToDouble(Double::doubleValue)
.sum();
System.out.println("总薪水(flatMap): " + totalSalary2);
// 3. 获取所有有部门名称的员工
List<String> departments = employees.stream()
.map(Employee::getDepartment)
.flatMap(Optional::stream)
.distinct()
.collect(Collectors.toList());
System.out.println("存在部门: " + departments);
// 4. 查找薪水最高的员工
Employee highestPaid = employees.stream()
.filter(emp -> emp.getSalary().isPresent())
.max((e1, e2) -> e1.getSalary().orElse(0.0)
.compareTo(e2.getSalary().orElse(0.0)))
.orElse(null);
System.out.println("最高薪员工: " + highestPaid);
}
static class Employee {
private String name;
private Double salary;
private String department;
public Employee(String name, Double salary, String department) {
this.name = name;
this.salary = salary;
this.department = department;
}
public String getName() { return name; }
public Optional<Double> getSalary() {
return Optional.ofNullable(salary);
}
public Optional<String> getDepartment() {
return Optional.ofNullable(department);
}
@Override
public String toString() {
return "Employee{" + name + ", " +
(salary != null ? salary : "无") + "}";
}
}
}
Optional最佳实践
1 应该避免的做法
public class OptionalAntiPatterns {
// 反模式1:使用isPresent()和get()(没有发挥Optional的优势)
public String badPractice1(Optional<String> optional) {
if (optional.isPresent()) {
return optional.get();
}
return "default";
}
// 反模式2:作为方法参数(应使用重载或明确的null检查)
public void badPractice2(Optional<String> name) {
// 不推荐在参数中使用Optional
}
// 反模式3:作为类的字段(序列化问题)
static class BadEntity {
private Optional<String> name; // 不应该这样做
}
// 反模式4:在方法链中过度使用Optional
public String badPractice4(String value) {
return Optional.ofNullable(value)
.map(String::trim)
.map(String::toUpperCase)
.orElse("");
}
// 反模式的替代方案
public String goodPractice(String value) {
// 直接用if判断,代码更清晰
if (value == null || value.trim().isEmpty()) {
return "";
}
return value.trim().toUpperCase();
}
// 反模式5:用Optional包装已经可能为null的返回值
public static class UserRepository {
// 错误示范
public Optional<User> findByIdBad(Long id) {
User user = databaseQuery(id); // 这里的user已经可能为null
return Optional.ofNullable(user);
}
// 正确方式:如果数据库的方法本身返回Optional
public Optional<User> findByIdGood(Long id) {
// 如果数据库查询API返回Optional,直接返回
return databaseQueryOptional(id);
}
private User databaseQuery(Long id) { return null; }
private Optional<User> databaseQueryOptional(Long id) {
return Optional.empty();
}
}
}
2 正确的使用模式
import java.util.Optional;
public class OptionalBestPractices {
// 最佳实践1:返回值使用Optional(明确表达可能为空)
public Optional<String> findUserName(Long userId) {
// 假设从数据库查询
String name = queryDatabase(userId);
return Optional.ofNullable(name);
}
// 最佳实践2:使用orElseThrow处理必须存在的值
public String getRequiredConfig(String key) {
return Optional.ofNullable(System.getProperty(key))
.orElseThrow(() -> new IllegalStateException("必须配置: " + key));
}
// 最佳实践3:链式转换和过滤
public String transformValue(Optional<String> input) {
return input
.map(String::trim)
.filter(s -> !s.isEmpty())
.orElse("default");
}
// 最佳实践4:使用flatMap避免嵌套Optional
public Optional<String> getNestedValue(Optional<Wrapper> wrapper) {
return wrapper.flatMap(Wrapper::getValue);
}
// 最佳实践5:ifPresentOrElse处理存在和不存在的情况
public void processOptional(Optional<String> optional) {
optional.ifPresentOrElse(
value -> System.out.println("处理: " + value),
() -> System.out.println("值为空时的处理")
);
}
private String queryDatabase(Long id) {
return null; // 模拟不存在的用户
}
static class Wrapper {
private String value;
public Optional<String> getValue() {
return Optional.ofNullable(value);
}
}
public static void main(String[] args) {
OptionalBestPractices example = new OptionalBestPractices();
example.processOptional(Optional.of("测试"));
example.processOptional(Optional.empty());
}
}
实际项目中的综合案例
import java.util.*;
public class OrderService {
// 订单状态
enum OrderStatus {
PENDING, PAID, SHIPPED, DELIVERED, CANCELLED
}
static class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() { return street; }
public String getCity() { return city; }
@Override
public String toString() {
return street + ", " + city;
}
}
static class Customer {
private Long id;
private String name;
private Address address; // 可能为null
public Customer(Long id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
public Long getId() { return id; }
public String getName() { return name; }
public Optional<Address> getAddress() {
return Optional.ofNullable(address);
}
}
static class Order {
private String orderId;
private Customer customer;
private double amount;
private OrderStatus status;
private Date deliveryDate; // 可能为null
public Order(String orderId, Customer customer, double amount,
OrderStatus status, Date deliveryDate) {
this.orderId = orderId;
this.customer = customer;
this.amount = amount;
this.status = status;
this.deliveryDate = deliveryDate;
}
public String getOrderId() { return orderId; }
public Optional<Customer> getCustomer() {
return Optional.ofNullable(customer);
}
public double getAmount() { return amount; }
public Optional<OrderStatus> getStatus() {
return Optional.ofNullable(status);
}
public Optional<Date> getDeliveryDate() {
return Optional.ofNullable(deliveryDate);
}
}
private List<Order> orders = new ArrayList<>();
public OrderService() {
// 初始化测试数据
Customer customer1 = new Customer(1L, "张三",
new Address("中关村大街1号", "北京"));
Customer customer2 = new Customer(2L, "李四", null);
orders.add(new Order("ORD001", customer1, 1999.00,
OrderStatus.DELIVERED, new Date()));
orders.add(new Order("ORD002", customer2, 399.00,
OrderStatus.PAID, null));
orders.add(new Order("ORD003", null, 899.00,
OrderStatus.PENDING, new Date()));
}
// 根据订单号查找订单
public Optional<Order> findOrderById(String orderId) {
return orders.stream()
.filter(order -> order.getOrderId().equals(orderId))
.findFirst();
}
// 获取订单的交付地址
public String getDeliveryAddress(String orderId) {
return findOrderById(orderId)
.flatMap(Order::getCustomer)
.flatMap(Customer::getAddress)
.map(Address::toString)
.orElse("无交付地址");
}
// 检查订单是否可取消
public boolean isOrderCancellable(String orderId) {
return findOrderById(orderId)
.map(Order::getStatus)
.flatMap(status -> status)
.map(status -> status == OrderStatus.PENDING ||
status == OrderStatus.PAID)
.orElse(false);
}
// 发送订单通知
public void sendOrderNotification(String orderId) {
Optional<Order> order = findOrderById(orderId);
order.ifPresentOrElse(
ord -> {
String customerName = ord.getCustomer()
.map(Customer::getName)
.orElse("未知客户");
System.out.println("发送订单通知给: " + customerName);
ord.getDeliveryDate().ifPresent(
date -> System.out.println("预计交付: " + date)
);
},
() -> System.out.println("订单不存在: " + orderId)
);
}
// 统计所有有交付日期的订单总额
public double calculateDeliveredAmount() {
return orders.stream()
.filter(order -> order.getStatus()
.map(s -> s == OrderStatus.DELIVERED)
.orElse(false))
.filter(order -> order.getDeliveryDate().isPresent())
.mapToDouble(Order::getAmount)
.sum();
}
public static void main(String[] args) {
OrderService service = new OrderService();
System.out.println("=== 订单地址查询 ===");
System.out.println("ORD001地址: " + service.getDeliveryAddress("ORD001"));
System.out.println("ORD002地址: " + service.getDeliveryAddress("ORD002"));
System.out.println("ORD003地址: " + service.getDeliveryAddress("ORD003"));
System.out.println("ORD999地址: " + service.getDeliveryAddress("ORD999"));
System.out.println("\n=== 订单取消检查 ===");
System.out.println("ORD001可取消: " + service.isOrderCancellable("ORD001"));
System.out.println("ORD002可取消: " + service.isOrderCancellable("ORD002"));
System.out.println("ORD003可取消: " + service.isOrderCancellable("ORD003"));
System.out.println("\n=== 发送通知 ===");
service.sendOrderNotification("ORD001");
service.sendOrderNotification("ORD002");
service.sendOrderNotification("ORD999");
System.out.println("\n=== 统计 ===");
System.out.println("已交付订单总额: " + service.calculateDeliveredAmount());
}
}
Optional核心方法总结
| 方法 | 说明 | 返回值 |
|---|---|---|
empty() |
创建空Optional | Optional<T> |
of(value) |
创建包含非null值的Optional | Optional<T> |
ofNullable(value) |
创建可能为null的Optional | Optional<T> |
isPresent() |
判断是否有值 | boolean |
ifPresent(consumer) |
如果有值则执行操作 | void |
ifPresentOrElse(consumer, runnable) |
有值/无值分别执行 | void |
get() |
获取值(无值抛异常) | T |
orElse(default) |
获取值或返回默认值 | T |
orElseGet(supplier) |
获取值或计算默认值 | T |
orElseThrow(exception) |
获取值或抛异常 | T |
map(mapper) |
转换值 | Optional<U> |
flatMap(mapper) |
转换值(不嵌套) | Optional<U> |
filter(predicate) |
过滤值 | Optional<T> |
stream() |
转换为流(Java 9+) | Stream<T> |
这个案例覆盖了Optional在实际开发中的主要使用场景,使用Optional可以让代码更安全、更清晰,但也要注意避免过度使用,在合适的场景下使用Optional是提升代码质量的好方法。