collections.unmodifiablelist创建的是只读视图而非不可变副本,它包装原列表并拦截修改操作抛出unsupportedoperationexception,但不阻止原始列表被修改。

用 Collections.unmodifiableList 创建只读视图,本质是包装原列表、拦截所有修改操作并抛出 UnsupportedOperationException,但它不阻止原始列表被修改——这是最容易踩的坑。
只读的是“视图”,不是“原数据”
unmodifiableList 返回的是一个代理对象,它把 get、size、iterator 等查询方法委托给底层列表,但所有 add、remove、set、clear 等方法都会直接抛异常。关键点在于:它不复制数据,也不冻结源列表。
- 如果后续代码仍持有原始
ArrayList引用,就能照常增删改 - 只读视图看到的“最新状态”,取决于源列表是否被改动
- 适合场景:对外提供安全访问入口,同时内部仍需维护数据
正确用法:切断原始引用暴露
要真正防止越权修改,必须确保调用方拿不到原始可变列表的引用。
- 不要返回私有字段本身(如
return this.items;) - 应在 getter 中即时封装:
return Collections.unmodifiableList(new ArrayList(this.items)); - 或更推荐:初始化时就用不可变容器(如
List.copyOf(this.items),Java 10+) - 若源列表生命周期短,也可在构造时深拷贝再包装
替代方案对比:什么时候该换别的方法
unmodifiableList 是轻量级防护,但有局限:
- 无法防御反射或序列化绕过(虽极少见,但高安全场景需注意)
- 不支持嵌套结构的深层只读(比如 List 中的 Map 仍可变)
- Java 9+ 推荐优先用
List.of()或ImmutableList.copyOf()(Guava),它们创建的是真正不可变实例 - 若需线程安全 + 不可变,考虑
CopyOnWriteArrayList配合 unmodifiable 包装(但通常冗余)
调试时怎么确认真的“锁住了”?
写个简单测试最直观:
- 拿到返回的 unmodifiable 列表后,调用
list.add("x")—— 必须抛UnsupportedOperationException - 检查类名是否为
UnmodifiableRandomAccessList(或类似) - 用 IDE 调试时展开 list 字段,看 delegate 是否指向你的原始列表(验证是否只是包装)











