Collections.unmodifiableList包装ArrayList可获得运行时只读视图,不复制数据而拦截修改操作并抛UnsupportedOperationException;原始列表仍可内部修改,但外部无法增删改,且为浅只读。

直接用 Collections.unmodifiableList 包装 ArrayList,就能获得一个运行时不可修改的只读视图——它不复制底层数据,只是拦截所有修改操作并抛出 UnsupportedOperationException。
创建只读视图的基本写法
这是最常用也最安全的方式:先构造可变的 ArrayList,再用 unmodifiableList 封装,对外只暴露返回的只读列表。
- 原始 ArrayList 仍可内部修改(比如在类的私有方法里),但外部拿到的引用无法增删改
- 返回类型建议声明为
List<T>,而非ArrayList<T>,避免类型强转绕过保护 - 注意:只读视图是“浅只读”——如果元素本身是可变对象(比如自定义类),其内部状态仍可被修改
典型封装模式(推荐在类中使用)
把 ArrayList 设为 private,提供一个返回只读视图的 public 方法:
private final List<String> items = new ArrayList<>();
public List<String> getItems() {
return Collections.unmodifiableList(items);
}
这样调用方即使拿到返回值,执行 add()、remove()、set() 都会立即抛异常,而类内部仍可自由维护 items。
立即学习“Java免费学习笔记(深入)”;
注意事项和常见陷阱
-
不要缓存或复用 unmodifiableList 返回值:每次调用
unmodifiableList都生成新代理对象,但开销极小;若缓存了旧视图,后续对原 ArrayList 的修改会反映在该视图中(这是预期行为,不是 bug) - 不能阻止反射或序列化攻击:这只是一种运行时防护,不防反射强行修改,也不解决反序列化时绕过的问题
-
与不可变集合库的区别:比如 Guava 的
ImmutableList或 JDK 10+ 的List.of()是真正不可变(不可扩展、不可 null 元素、线程安全发布),而unmodifiableList只是包装器
替代方案参考(按场景选)
- 如果列表构建后就不再变化,优先用
List.of(...)(JDK 9+)或Arrays.asList(...)(注意后者底层仍是可变数组) - 需要深度不可变(含元素不可变)?需自行确保元素类型是不可变类,或用更严格的封装(如返回副本、或用 record 类型)
- 高频读、低频写的场景,也可考虑
CopyOnWriteArrayList,但它不是只读,而是线程安全的写时复制


















