引用类型数组默认是浅拷贝,只复制引用地址而非对象本身;深拷贝需手动实现,如克隆、序列化或构造新对象;基本类型数组拷贝的是值,天然独立。

Java 数组拷贝中,引用类型数组的“引用拷贝”和“内容拷贝”根本不是一回事:前者只复制地址,后者才真正复制对象数据。关键不在怎么写代码,而在你是否清楚自己到底想隔离数据,还是只想共享对象。
引用类型数组的默认拷贝是浅拷贝
用 System.arraycopy、Arrays.copyOf 或 clone() 拷贝 String[]、User[] 等引用类型数组时,实际复制的是每个元素的引用(内存地址),不是对象本身。
- 两个数组长度相同、索引一一对应,但所有元素指向堆中同一组对象
- 修改
arr2[0].setName("新名字"),arr1[0]的名字也会变——因为它们是同一个 User 实例 - String 是特例:它不可变,
arr2[0] = "hello"只是让 arr2[0] 指向新字符串,不影响 arr1[0],但这属于赋值行为,不是拷贝逻辑带来的效果
内容拷贝 = 深拷贝,必须手动实现
要让新数组里的对象和原数组完全独立,就得对每个元素做深拷贝。没有通用一行代码能搞定,取决于对象是否支持克隆或序列化。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 若元素类实现了
Cloneable且重写了clone()(并确保内部引用也 clone),可遍历调用:dest[i] = (T) src[i].clone() - 若类实现了
Serializable,可用序列化方式:把整个数组对象写入字节流再读出,天然递归深拷贝(注意性能开销和 transient 字段) - 最稳妥但略繁琐的方式:用构造函数逐个新建对象,例如
dest[i] = new User(src[i].getName(), src[i].getAge())
基本类型数组不存在这个问题
int[]、double[] 这类数组拷贝后,源与目标彻底无关——因为拷贝的是数值本身,不是地址。
立即学习“Java免费学习笔记(深入)”;
-
System.arraycopy对基本类型就是值拷贝,安全高效 - 改
dest[0] = 99,src[0]完全不受影响 - 所以别把基本类型的经验套到引用类型上,这是最容易踩的坑
怎么选?看你的业务意图
拷贝不是技术动作,而是数据契约。问自己一句:这个副本要不要和原件“解耦”?
- 只是临时读取、不修改对象内容 → 浅拷贝足够,快且省内存
- 要并发修改、做状态快照、防止副作用 → 必须深拷贝,哪怕多写几行
- 不确定?先用
System.identityHashCode(obj)打印几个元素的哈希码,对比源数组和目标数组同位置元素是否一致,一眼验证是浅是深

















