聊聊Java中的of
深入理解 Java 中的 of() 方法:工厂模式的优雅实践
在 Java 的演进历程中,of() 方法已经成为了一种非常普遍且重要的设计模式。从 Java 8 引入的 Stream.of(),到 Java 9 大规模引入的集合工厂方法 List.of()、Set.of(),再到各种自定义业务对象中的使用,of() 已经成为 Java 开发者构建对象的“标准词汇”。
本篇教程将带你全面了解 of() 方法的前世今生、核心设计思想、常见应用场景,并通过丰富的案例掌握其用法。
一、什么是 of() 方法?它解决了什么问题?
of() 方法本质上是一种静态工厂方法。它提供了一种创建对象的简洁方式,替代了传统的 new 关键字。
在没有 of() 方法之前,我们创建集合通常是这样的:
// 传统方式:繁琐,可变集合
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
使用 of() 方法后:
// 现代方式:简洁,不可变集合
List<String> list = List.of("A", "B", "C");
of() 的核心优势:
简洁性:一行代码完成对象初始化,省去了繁琐的 new 和 add 操作。
不可变性:Java 9 引入的 List.of() 等创建的都是不可变集合,避免了无意间的修改,线程安全。
语义清晰:of 本身就有“由…组成”的意思,见名知义,代码可读性极高。
隐藏实现细节:你不需要知道具体的实现类(如 ArrayList 还是 HashSet),只需要关心接口。
性能优化:底层可能根据元素数量返回特定的优化实现(如空集合、单元素集合的特化实现)。
二、Java 核心库中的 of() 大全与案例
1. Java 8: Stream 和 Optional 的 of()
Java 8 引入函数式编程,of() 开始大放异彩。
案例:快速创建流
// 创建单个元素的 Stream
Stream<String> singleStream = Stream.of("Java");
// 创建多个元素的 Stream
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 注意:传入数组
String[] arr = {"Spring", "Boot"};
Stream<String> arrayStream = Stream.of(arr);
案例:快速创建 Optional
// 创建一个非空的 Optional
Optional<String> opt = Optional.of("Hello");
// ⚠️ 严禁传入 null,否则抛出 NullPointerException
// Optional<String> nullOpt = Optional.of(null); // 运行时报错!
2. Java 9+: 集合的不可变工厂方法 of()
这是 of() 方法在 Java 中最著名的应用。Java 9 为 List、Set、Map 接口引入了静态工厂方法。
案例:创建不可变 List
List<String> fruits = List.of("Apple", "Banana", "Orange");
// ⚠️ 以下操作会抛出 UnsupportedOperationException
// fruits.add("Grape"); // 报错:不能增加
// fruits.remove(0); // 报错:不能删除
// fruits.set(0, "Pear"); // 报错:不能修改
案例:创建不可变 Set
Set<Integer> numbers = Set.of(1, 2, 3, 4, 5);
// ⚠️ Set 不允许重复元素,如果传入重复元素,直接抛出 IllegalArgumentException
// Set<Integer> duplicateSet = Set.of(1, 1, 2); // 运行时报错!
案例:创建不可变 Map
Map.of() 最多支持 10 个键值对。如果超过 10 个,需要使用 Map.ofEntries()。
// 10个以内的键值对
Map<String, Integer> ages = Map.of(
"Alice", 25,
"Bob", 30,
"Charlie", 28
);
// 超过10个键值对使用 Map.ofEntries()
import java.util.Map.Entry;
Map<String, Integer> largeMap = Map.ofEntries(
Entry.of("K1", 1),
Entry.of("K2", 2),
// ... 省略
Entry.of("K11", 11)
);
3. 其他常见 API 中的 of()
LocalDate / LocalDateTime:创建日期时间对象。LocalDate date = LocalDate.of(2023, 10, 1);
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 1, 12, 0, 0);
Path:创建文件路径。Path path = Path.of("/usr/local/bin");
Pattern:编译正则表达式。Pattern pattern = Pattern.of("[a-z]+"); // Java 21+ 引入
三、进阶实战:在业务代码中自定义 of() 方法
of() 不仅仅属于 JDK,在我们的日常业务开发中,它也是替代冗长构造器的利器。特别是在定义 DTO、VO 或不可变实体时。
案例:定义一个不可变的用户 DTO
传统做法:使用构造器
public class UserDTO {
private final String username;
private final String email;
private final Integer age;
// 冗长的构造器
public UserDTO(String username, String email, Integer age) {
this.username = username;
this.email = email;
this.age = age;
}
// ... getters
}
// 调用:看起来不够清晰,特别是类型相似的参数
UserDTO user = new UserDTO("admin", "admin@abc.com", 25);
优雅做法:使用 of() 静态工厂方法
public class UserDTO {
private final String username;
private final String email;
private final Integer age;
// 私有化构造器,强制使用工厂方法
private UserDTO(String username, String email, Integer age) {
this.username = username;
this.email = email;
this.age = age;
}
// 提供 of 方法
public static UserDTO of(String username, String email, Integer age) {
return new UserDTO(username, email, age);
}
// ... getters
}
// 调用:语义更顺畅,有“由这些属性组成一个用户”的感觉
UserDTO user = UserDTO.of("admin", "admin@abc.com", 25);
案例:带有缓存逻辑的 of() 方法
静态工厂方法的优势在于:它不一定要每次都 new 一个新对象。我们可以根据参数返回缓存实例,这是构造器做不到的。
public class Role {
private String name;
private static final Map<String, Role> CACHE = new HashMap<>();
private Role(String name) {
this.name = name;
}
public static Role of(String name) {
// 如果缓存中有,直接返回缓存对象;没有则创建并放入缓存
return CACHE.computeIfAbsent(name, Role::new);
}
}
// 调用:多次 of 相同的 name,返回的是同一个对象
Role admin1 = Role.of("ADMIN");
Role admin2 = Role.of("ADMIN");
System.out.println(admin1 == admin2); // true
四、避坑指南:使用 of() 的注意事项
of() 创建的集合是不可变的:这是最容易踩坑的地方。如果你需要对集合进行增删改操作,千万不要直接用 List.of()。可以先创建不可变集合,再包装成可变集合:
List<String> mutableList = new ArrayList<>(List.of("A", "B"));
mutableList.add("C"); // 正常运行
Map.of() 和 Set.of() 不允许 null 键或值:如果尝试传入 null,会直接抛出 NullPointerException。而传统 HashMap 是允许 null 键和值的。
Set.of() 对重复元素零容忍:在运行时直接抛出 IllegalArgumentException,而不是像传统 HashSet 那样默默覆盖。
参数数量限制:Stream.of() 如果传入多个基本类型数组,会把整个数组当作一个元素处理(泛型擦除问题),处理基本类型数组请用 Arrays.stream()。
命名规范:在业务代码中,除了 of,有时也会用 valueOf(如 String.valueOf())或 from(通常表示类型转换,如从 DTO 转为 Entity:UserEntity.from(dto))。建议纯构建对象用 of,对象转换用 from。
五、总结
of() 方法看似微不足道,实则是 Java 走向现代化、函数式编程的重要标志。
在日常开发中,当你需要创建一个不可变的数据载体,或者想要隐藏对象复杂的初始化逻辑时,请毫不犹豫地拿起 of() 这个优雅的武器吧!
更多推荐

所有评论(0)