list.of 创建不可变列表时传入 null 会立即抛出 nullpointerexception;collections.unmodifiablelist 则仅提供只读视图,不阻止底层修改,且不递归冻结嵌套对象。

用 List.of 创建不可变列表时,空值会直接报错
Java 9 引入的 List.of 是最轻量的不可变列表创建方式,但它在构造阶段就做严格校验:只要传入 null,立刻抛出 NullPointerException。这不是运行时保护,而是构造期拦截。
- 适用场景:确定元素全非 null、数量固定(最多 10 个参数),且不需要后续修改语义
- 不适用场景:从数据库或外部 API 读取数据后直接转不可变集合(可能含 null)、需要动态构建再冻结
-
List.of("a", "b", null)→ 抛出NullPointerException;而Collections.unmodifiableList(Arrays.asList("a", "b", null))能成功创建,但访问时才暴露 null 风险
Collections.unmodifiableList 包装的是“视图”,不是新集合
它返回的只是一个原列表的只读代理,底层引用没变。如果原始 List 被其他代码修改,不可变视图里的内容也会跟着变——只是你不能通过这个视图改它。
- 常见错误现象:
var raw = new ArrayList(Arrays.asList("x")); var unmod = Collections.unmodifiableList(raw); raw.add("y"); System.out.println(unmod);输出[x, y] - 真正安全的做法是先复制再包装:
Collections.unmodifiableList(new ArrayList(source)),但要注意深浅拷贝问题(若元素本身可变,仍可能被外部修改) - 性能影响:零拷贝、无额外内存开销,但失去了“彻底隔离”的保障
两者在序列化和泛型推断上行为不同
List.of 返回的是私有静态内部类(如 ImmutableCollections.ListN),实现了 Serializable,但某些老框架(如早期 Jackson 版本)可能因反射限制无法识别其构造器;Collections.unmodifiableList 返回的是 UnmodifiableRandomAccessList,继承链更“标准”,兼容性略好。
- 泛型推断差异:
List.of(1, 2L)编译失败(类型不一致),而Collections.unmodifiableList(Arrays.asList(1, 2L))可以,因为Arrays.asList推出List<object></object> - 序列化风险:若用
List.of创建的实例被写入 Redis 或跨服务传输,需确认下游反序列化环境支持 Java 9+ 的不可变集合实现
嵌套不可变结构必须逐层冻结
不可变性不会自动穿透。用 List.of 包一层 Map,只保证列表本身不可增删,但里面每个 Map 仍可被修改;同理,unmodifiableList 也不递归保护元素。
- 容易踩的坑:
List.of(Map.of("k", "v"))→ 外层列表不可变,但get(0).put("k2", "v2")依然合法 - 正确做法:若需完全冻结,得手动确保每个元素也不可变,比如用
Map.of替代new HashMap(),或对已有对象调用Map.copyOf(Java 10+) - 没有银弹:Java 原生不提供深度不可变工具,复杂嵌套建议用
immutables.org或Guava的ImmutableList.copyOf配合自定义 builder
真正麻烦的从来不是“怎么标不可变”,而是“怎么让整个对象图里每一层都不可变”——这点连 List.of 和 Collections.unmodifiableList 都不帮你兜底。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











