数组拷贝的核心是隔离外部对内部状态的修改,构造器需立即深拷贝传入数组,getter不可返回原始引用,引用类型数组须递归深拷贝元素,final和unmodifiableList无法防止对象内部变更。

数组拷贝不是为了“复制而复制”,而是为了切断外部对内部状态的意外修改路径。关键不在复制动作本身,而在明确谁持有可变性、谁可能触发变更。
构造器里接数组,必须立刻隔离
外部传来的数组一旦直接赋值给成员变量,就等于把内存地址钥匙交了出去。后续任何修改都会穿透到你对象内部。
- 基本类型数组(如 int[])用 Arrays.copyOf(arr, arr.length) 或 arr.clone() 即可
- 引用类型数组(如 Date[]、String[])仅拷贝数组本身还不够——每个元素仍是原对象引用,需额外对每个可变元素做深拷贝(例如 new Date(date.getTime()))
- 别依赖 final:它只锁住引用变量不重赋值,不阻止数组内容被改
getter 返回数组,不能交原始引用
直接 return internalArray 相当于公开了私有字段的后门。调用方拿到后增删改,你的对象状态就失控了。
- 每次 getter 都新建副本,虽安全但可能带来 GC 压力;更优做法是在构造或 setter 阶段就完成保护性拷贝,getter 只返回已隔离的副本
- 若数组内容极少变动,可考虑用不可变包装(如 Collections.unmodifiableList 配合 ArrayList 拷贝),但注意它不防底层对象被别处修改
- 优先选用不可变替代方案:比如用 List.of()、ImmutableList,或 Java 8+ 的 Instant/LocalDateTime 替代 Date
长期持有外部数组时,拷贝时机要前置
如果方法参数是数组,且你要缓存、校验后保存、或用于后续多次计算,就不能等用的时候再拷贝——外部可能在你不知情时已改过原数组。
- 一进入方法就执行拷贝,确保后续所有操作基于稳定快照
- 对嵌套结构(如 Person[] 中每个 Person 含可变字段),需递归深拷贝,不能只拷数组壳子
- 避免用 = 赋值,哪怕只是临时变量,也容易误传引用
别把“安全错觉”当防护
有些做法看似防御,实则无效,甚至掩盖真正风险。
- Collections.unmodifiableList 只拦调用方的 add/remove,拦不住他们拿到元素后改其内部状态(如 Date.setTime())
- 只在 getter 里拷贝,却在构造器放任原始引用入内,等于前门上锁后窗大开
- 用 Arrays.asList(arr) 包装原始数组?它背后仍是原数组,改 list 就改原数组


















