当前位置: 首页 > news >正文

Java四种拷贝方式总结!一文扫清所有拷贝问题

作为Java开发者,日常开发中经常会遇到数据拷贝的需求。最近在面试中也有被问到一次大文件拷贝,抽空专门总结一下,从基础概念到实战技巧,配合流程图,让原理和流程一目了然~

一、浅拷贝VS深拷贝

一开始做spring项目时,就被浅拷贝坑过。当时想复制一个对象,结果改了新对象的属性,老对象也跟着变了。后来才知道,这就是浅拷贝的“锅”。

浅拷贝就像拍证件照,照片上的人看着和你一模一样,但本质上还是两张纸。Java里的浅拷贝只会复制对象的基本属性,遇到引用类型(比如自定义类),就直接把地址抄过来。所以你改新对象里的引用属性,老对象也会跟着遭殃。

深拷贝就靠谱多了,它会把对象里里外外都复制一遍,就像克隆人,新对象和老对象完全独立。虽然麻烦点,但胜在安全,适合处理复杂对象。

引用类型属性
共用引用
引用类型属性
重新创建属性对象
原对象
浅拷贝对象
属性对象
原对象
深拷贝对象
属性对象
新属性对象

举个例子,有个Person类包含nameAddress属性:

class Address {String city;
}class Person {String name;Address address;// 浅拷贝Person shallowCopy() {Person copy = new Person();copy.name = this.name;copy.address = this.address;return copy;}// 深拷贝Person deepCopy() {Person copy = new Person();copy.name = this.name;copy.address = new Address();copy.address.city = this.address.city;return copy;}
}

二、大文件拷贝:别让你的程序“龟速运行”

之前做文件上传功能,用传统IO方式拷贝大文件,结果服务器直接卡住。后来换成NIO,速度直接起飞!

传统IO就像蚂蚁搬家,一次搬一点,频繁读写磁盘;NIO则像卡车运输,直接把数据从一个地方搬到另一个地方,速度快得多。

NIO拷贝
传统IO拷贝
数据未读完?
获取目标文件通道
获取源文件通道
直接传输数据
关闭通道
读取数据到缓冲区
打开源文件输入流
写入目标文件输出流
关闭流
// 传统IO方式,适合小文件
public static void copyFileByIO(File source, File dest) throws IOException {try (InputStream is = new FileInputStream(source);OutputStream os = new FileOutputStream(dest)) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {os.write(buffer, 0, len);}}
}// NIO方式,适合大文件
public static void copyFileByNIO(File source, File dest) throws IOException {try (FileChannel sourceChannel = new FileInputStream(source).getChannel();FileChannel destChannel = new FileOutputStream(dest).getChannel()) {destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());}
}

三、对象拷贝的进阶方案:使用造好的轮子

手动写深拷贝代码太麻烦?别慌,Java提供给我们有不少写好的办法。

1. 序列化与反序列化

就像把对象打包成快递,寄出去再拆开。虽然速度慢点,但能保证完全独立的拷贝。不过要注意,所有相关类都得实现Serializable接口。

原对象
序列化
字节数组
反序列化
新对象
public static <T extends Serializable> T deepCopyBySerialization(T obj) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {oos.writeObject(obj);try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais)) {return (T) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException(e);}
}
2. JSON序列化

用Jackson或Gson把对象转成JSON字符串,再转回来,简单粗暴。不过这种方式要求对象的属性都能被JSON正确解析。

原对象
转JSON字符串
解析JSON
新对象
import com.fasterxml.jackson.databind.ObjectMapper;public static <T> T deepCopyByJSON(T obj, Class<T> clazz) {try {ObjectMapper mapper = new ObjectMapper();String json = mapper.writeValueAsString(obj);return mapper.readValue(json, clazz);} catch (Exception e) {throw new RuntimeException(e);}
}

四、数组和集合的拷贝

数组的clone()方法看似简单,实则暗藏玄机:基本类型数组用它是深拷贝,对象数组就是浅拷贝。

对象数组
基本类型数组
元素值
独立复制值
对象引用
共用引用
clone方法
原数组
新数组
对象1
clone方法
原数组
新数组
值1
int[] intArray = {1, 2, 3};
int[] copiedIntArray = intArray.clone(); // 深拷贝Person[] personArray = new Person[2];
Person[] copiedPersonArray = personArray.clone(); // 浅拷贝

集合类的构造函数也是浅拷贝,比如new ArrayList<>(originalList)。想深拷贝集合,得手动遍历每个元素。

原集合
遍历元素
深拷贝每个元素
添加到新集合
新集合
List<Person> originalList = new ArrayList<>();
List<Person> deepCopiedList = originalList.stream().map(Person::deepCopy).collect(Collectors.toList());

五、第三方库:站在巨人的肩膀上

不想自己造轮子?Apache Commons Lang和Dozer能帮你大忙。前者提供了便捷的序列化拷贝工具,后者擅长对象属性映射,用起来超方便。

<!-- Apache Commons Lang依赖 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version>
</dependency>// 使用示例
Person copiedPerson = SerializationUtils.clone(originalPerson);

六、总结:看不同场景选择合适的拷贝方式

  • 简单对象用浅拷贝,复杂对象用深拷贝
  • 大文件优先用NIO,小文件用传统IO
  • 能偷懒就偷懒,善用第三方库
  • 多写测试用例,别让拷贝“埋雷”

希望这篇文章能帮你理清Java拷贝的思路。要是还有疑问,欢迎在评论区交流!

http://www.lqws.cn/news/528103.html

相关文章:

  • npm run dev报错
  • 软件安装——下载安装ollama
  • leetcode 65
  • Autosar方法论
  • 力扣2311:小于等于K的最长二进制子序列
  • 【TIDB】了解,MySQL和TiDB的取舍,差异
  • postman设置接口关联,实现参数化
  • kotlin中::class.java的意义
  • Redis 为什么选用跳跃表,而不是红黑树
  • PHP基础2(流程控制,函数)
  • 【机器学习深度学习】交互式线性回归 demo
  • C语言再出发:2025年AI时代的关键语言
  • notepad++ 怎么快速给 python (nginx、shell) 文件加 # 注释
  • VUE3入门很简单(3)--- watch
  • MR30分布式 IO在物流堆垛机的应用
  • 解锁AI无限潜能!景联文科技数据产品矩阵再升级:多语言题库、海量语料、垂域代码库,全面赋能大模型训练
  • 力扣第45题-跳跃游戏2
  • 【智能记录系统Blinko】从0到1搭建个人云端笔记本:Blinko+Docker环境配置
  • JVM OutOfMemoryError原因及排查解决方案
  • java解决超大二维矩阵数组引起的内存占用过大问题
  • 深入解析synchronized实现原理
  • 【2-入门与调试设置】1.坐标辅助器与轨道控制器
  • 英特尔汽车业务败走中国,喊出“All in”才过两个月
  • 观测云产品更新 | 外部数据源、日志、监控、事件、基础设施等
  • TCP 协议安全性全面分析:漏洞、应用场景与防护策略
  • 芯谷科技--降压型DC-DC转换器D4005
  • [OS_27] 现代应用程序架构
  • ESP32 VSCODE进入menuconfig时ESP-IDF idf.py menuconfig卡进度条,setuptools版本太高解决方法
  • 小程序学习笔记:实现上拉触底加载随机颜色案例全解析
  • 深度剖析 Apache Pulsar:架构、优势与选型指南