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

深克隆与浅克隆的区别与实现

在软件开发中,克隆对象是一个常见需求。克隆的方式主要有两种:深克隆(Deep Clone)和浅克隆(Shallow Clone)。了解它们的区别及其实现方法,对于编写高效、安全的代码非常重要。

深克隆与浅克隆的区别

浅克隆(Shallow Clone)
浅克隆会复制原型对象的基本数据类型的字段(如int, float等),而对于引用类型的字段(如对象、数组等),只会复制其引用地址。也就是说,原型对象和克隆对象会共享引用类型的字段。

深克隆(Deep Clone)
深克隆不仅复制原型对象的基本数据类型字段,还会递归复制引用类型的字段。这样,原型对象和克隆对象在内存中是完全独立的,不会共享任何引用类型的字段。

如何实现深克隆?

深克隆的实现方式有多种,下面介绍三种常见的方法:

  1. 所有对象都实现克隆方法
  2. 通过构造方法实现深克隆
  3. 使用 JDK 自带的字节流实现深克隆
所有对象都实现克隆方法

这种方式要求所有引用类型的对象都实现Cloneable接口,并重写clone方法。例如:

public class CloneExample {public static void main(String[] args) throws CloneNotSupportedException {// 创建被赋值对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 克隆 p1 对象People p2 = p1.clone();// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class People implements Cloneable {private Integer id;private String name;private Address address;@Overrideprotected People clone() throws CloneNotSupportedException {People people = (People) super.clone();people.setAddress(this.address.clone()); // 引用类型克隆赋值return people;}// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address implements Cloneable {private Integer id;private String city;@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address) super.clone();}// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}
通过构造方法实现深克隆

《Effective Java》中推荐使用构造器来实现深克隆。构造器的参数为基本数据类型或字符串类型时直接赋值,如果是对象类型,则需要重新创建一个新的对象。

public class SecondExample {public static void main(String[] args) {// 创建对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 调用构造函数克隆对象People p2 = new People(p1.getId(), p1.getName(), new Address(p1.getAddress().getId(), p1.getAddress().getCity()));// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class People {private Integer id;private String name;private Address address;// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address {private Integer id;private String city;// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}
使用 JDK 自带的字节流实现深克隆

通过字节流实现深克隆的方式是将原型对象写入到内存中的字节流,然后再从这个字节流中读出信息,生成一个新对象。这个新对象与原型对象在内存地址上是完全独立的。

import java.io.*;public class ThirdExample {public static void main(String[] args) {// 创建对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 通过字节流实现克隆People p2 = (People) StreamClone.clone(p1);// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class StreamClone {public static <T extends Serializable> T clone(People obj) {T cloneObj = null;try {// 写入字节流ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bo);oos.writeObject(obj);oos.close();// 分配内存, 写入原始对象, 生成新对象ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);// 返回生成的新对象cloneObj = (T) oi.readObject();oi.close();} catch (Exception e) {e.printStackTrace();}return cloneObj;}}static class People implements Serializable {private Integer id;private String name;private Address address;// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address implements Serializable {private Integer id;private String city;// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}

需要注意的是,由于通过字节流序列化实现的深克隆,每个对象必须实现Serializable接口,否则会抛出异常。

总结

深克隆和浅克隆在对象复制上的区别主要在于是否复制引用类型的对象。浅克隆仅复制对象本身,而深克隆会递归复制所有引用类型的对象。根据需求的不同,可以选择实现Cloneable接口、使用构造器或者通过字节流进行深克隆。了解这些实现方法,可以帮助我们在开发过程中更好地管理对象的复制和内存的使用。

http://www.lryc.cn/news/404261.html

相关文章:

  • 【学习笔记】无人机系统(UAS)的连接、识别和跟踪(六)-无人机直接C2通信
  • 认识和安装R的扩展包,什么是模糊搜索安装,工作目录和空间的区别与设置
  • 解决STM32开启定时器时立即进入一次中断程序问题
  • Unity UGUI 之EventSystem
  • USB转多路UART - USB 基础
  • 接近50个实用编程相关学习资源网站
  • 在数据操作中使用SELECT子句
  • Golang | Leetcode Golang题解之第274题H指数
  • 区块链技术在智能家居中的创新应用探索
  • 无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力
  • PHP 表单验证:邮件和URL
  • 前端八股文 路由的懒加载
  • HarmonyOS Web组件(二)
  • HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号2
  • 基于python深度学习遥感影像地物分类与目标识别、分割实践技术应用
  • 叶再豪降龙精英课程总结
  • 算法 - 查找算法(顺序、折半、红黑树、AVL树、B+树、散列)
  • TCP与UDP网络编程
  • 媲美Midjourney-v6,Kolors最新文生图模型部署
  • 深度学习程序环境配置
  • 【STM32 HAL库】全双工I2S+双缓冲DMA的使用
  • 【Spring Boot】网页五子棋项目中遇到的困难及解决方法
  • 营销策划方案模板
  • Python入门基础教程(非常详细)
  • LeetCode 常见题型汇总
  • el-select选择器修改背景颜色
  • Shell程序设计
  • PyQT6---环境搭建
  • whisper-api语音识别语音翻译高性能兼容openai接口协议的开源项目
  • 面试题:Java中堆内存和栈内存的区别,缓存数据是把数据放到哪里