基于java实现浅拷贝和深拷贝

article/2025/10/5 14:14:54

目录

  • 1、概念
  • 2、浅拷贝
    • 2.1、浅拷贝实战
  • 3、深拷贝
    • 3.1、嵌套 clone 方法
    • 3.2、使用序列化流
    • 3.3、使用开源工具类

1、概念

浅拷贝:在拷贝一个对象时,复制基本数据类型的成员变量,但对引用数据类型的成员变量只进行引用的传递(复制其地址引用),并不会创建一个新的对象。简单地说就是被拷贝对象和浅拷贝得到的新对象,它们的引用数据类型的成员变量指向同一个内存地址。

深拷贝:在拷贝一个对象时,除了复制基本数据类型的成员变量,对引用数据类型的成员变量进行拷贝时,会创建一个新的对象来保存引用类型的成员变量。
简单地说就是被拷贝对象和深拷贝得到的新对象,它们的引用数据类型的成员变量指向不同的内存地址。

定义如下类:

class Person {private int age;private StringBuffer name;
}

对 Person 类的对象拷贝时,浅拷贝和深拷贝示意图区别如下:
在这里插入图片描述


2、浅拷贝

Java已经内置了 Cloneable 抽象原型接口,自定义的类型只需实现该接口并重写 Object.clone() 方法即可完成本类的浅拷贝。

Cloneable 是一个空接口。Java之所以提供 Cloneable 接口,只是为了在运行时通知Java虚拟机可以安全地在该类上使用 clone() 方法。而如果该类没有实现 Cloneable 接口,则调用 clone() 方法会抛出 CloneNotSupportedException 异常。

一般情况下,如果使用 clone() 方法,则需满足以下条件:
    1、对任何对象 o,都有 o.clone() != o。换言之,克隆对象与原型对象不是同一个对象;
    2、对任何对象 o,都有 o.clone().getClass() == o.getClass()。换言之,复制对象与原对象的类型一样;
    3、如果对象 o 的 equals() 方法定义恰当,则 o.clone().equals(o) 应当成立。

2.1、浅拷贝实战

假设,现在有两个类,学生 Student 和班级 Class,每个学生都有自己对应的班级:

/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class {private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Cloneable{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;/*** 重写 Object.clone()方法*/@Overridepublic Student clone() throws CloneNotSupportedException {/*** super.clone()方法直接从堆内存中以二进制流的方式进行复制,重新分配一个内存块,其效率很高;* 由于super.clone()方法基于内存复制,不会调用对象的构造函数,也就是不需要经历初始化过程;* 使用super.clone()方法,如果类中存在引用对象属性,则原型对象与克隆对象的该属性会指向同一对象的引用。*/return (Student)super.clone();}
}

浅拷贝测试:

/*** @Des 浅拷贝测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级", "一年级的学生比较调皮");Student student = new Student(1, "基地", 7, classs);// 浅拷贝对象Student clone = student.clone();// 结果为 true,指向的是同一个内存地址System.out.println(student.getClasss() == clone.getClasss());}
}

3、深拷贝

深拷贝可以有以下几种不同的实现方式:
    1、嵌套 clone 方法:在需要克隆的对象以及该对象的引用类型的变量的类中全部实现 cloneable 接口,但是对于层级比较深的对象,不太友好;
    2、使用序列化流:使要序列化的对象和该对象的引用类型成员变量对象的类都实现 Serializable 接口,将对象序列化到输出流中,然后再反序列化为对象就完成了完全的复制操作。但是静态的成员和 transient 关键字修饰的成员不能被序列化;
    3、使用开源工具类,例如,json类库(FastJson,GSON等将对象转化为json字符串,然后将json字符串转换为对象),Spring的BeanUtils,Cglib的BeanCopier。

3.1、嵌套 clone 方法

定义实体类 Student 和 Class,两个实体类都实现 Cloneable 接口并重写 Object.clone(),对于引用数据类型的变量继续使用其重写的 clone 方法进行拷贝:

/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class implements Cloneable{private int id;private String name;private String desc;/*** 重写 Object.clone()方法*/@Overridepublic Class clone() throws CloneNotSupportedException {return (Class)super.clone();}
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Cloneable{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;/*** 重写 Object.clone()方法*/@Overridepublic Student clone() throws CloneNotSupportedException {Student student = (Student)super.clone();/*** 对于引用数据类型,继续调用clone方法* 如果引用数据类型嵌套层级很多,每个层级都需要处理*/student.classs = this.classs.clone();return student;}
}

测试:

/*** @Des 深拷贝测试(嵌套 clone)* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 深拷贝对象Student clone = student.clone();// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}

3.2、使用序列化流

定义序列化工具类 SerialCloneUtils :

/*** @Des* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/12*/
@Slf4j
public class SerialCloneUtils {/*** @Desc 使用ObjectStream序列化实现深克隆* @Author jidi* @date 2022/7/12 9:52*/public static <T extends Serializable> T deepClone(T t) throws CloneNotSupportedException {ByteArrayOutputStream bout = null;ObjectOutputStream out = null;InputStream bin = null;ObjectInputStream in = null;try {// 保存对象为字节数组bout = new ByteArrayOutputStream();out = new ObjectOutputStream(bout);out.writeObject(t);// 从字节数组中读取克隆对象bin = new ByteArrayInputStream(bout.toByteArray());in = new ObjectInputStream(bin);return (T)(in.readObject());}catch (IOException | ClassNotFoundException e){CloneNotSupportedException cloneNotSupportedException = new CloneNotSupportedException();e.initCause(cloneNotSupportedException);throw cloneNotSupportedException;}finally {try {if (Objects.nonNull(bout)){bout.close();}if (Objects.nonNull(out)){out.close();}if (Objects.nonNull(bin)){bin.close();}if (Objects.nonNull(in)){in.close();}} catch (IOException e) {log.error("关闭IO流失败:{}", e);}}}
}

创建一个公共父类 SerialClone ,只要继承该类就可以实现深克隆:

/*** @Des* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/12*/
public class SerialClone implements Cloneable, Serializable {private static final long serialVersionUID = -3556726131986995463L;@Overridepublic Object clone() throws CloneNotSupportedException {return SerialCloneUtils.deepClone(this);}}

定义实体类 Student 和 Class,分别继承 SerialClone 类:

/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class extends SerialClone{private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student extends SerialClone{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;
}

测试:

/*** @Des 深复制测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 深拷贝对象Student clone = (Student)student.clone();// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}

3.3、使用开源工具类

此处使用 FastJson 实现深拷贝:

/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class{private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;
}/*** @Des 深复制测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 使用 FastJson 深拷贝对象Student clone = JSONObject.parseObject(JSONObject.toJSONBytes(student), Student.class);// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}

可以看到使用JSON类库进行深拷贝时,实体类不需要实现任何接口即可。但是需要注意的是,不同的JSON类库序列化规则不一样,对于某些表示是否的变量,命名时最好不要使用 isXxx 形式,否则会导致序列化失败。


http://chatgpt.dhexx.cn/article/RFtIF8M9.shtml

相关文章

js实现浅拷贝和深拷贝

一.数据类型 数据分为基本数据类型和引用数据类型 基本数据类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol) 引用数据类型&#xff08;Object[Array属于Object]&#xff09; 基本数据类型的特点&#xff1a;直接存储在栈(stack)中的数据 引用数据类型的特点…

浅拷贝和深拷贝

最近学习string类的时候感觉这里概念有点混淆&#xff0c;浅拷贝与深拷贝的区别&#xff0c;上网查了一下&#xff0c;原来没我想 的难么复杂&#xff0c;以下是我的理解&#xff0c;如果有不对的地方求大佬留言交流交流。 浅拷贝&#xff1a; 顾名思义就是浅层的拷贝&#xff…

理解浅拷贝和深拷贝以及实现方法

一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol)和引用数据类型Object&#xff0c;包含&#xff08;function&#xff0c;Array&#xff0c;Date&#xff09;。 1、基本数据类型的特点&#xff1a;直接存储在栈内存中的数据 …

VUE浅拷贝和深拷贝

文章目录 前言一、数据类型1.1.基本数据类型1.2.引用数据类型1.3.区别 二、浅拷贝2.1.定义2.2.浅拷贝特点 三、深拷贝3.1.定义3.2.深拷贝特点 四、拷贝实现方案4.1.Object.assign()4.2.concat()4.3.slice()4.4.JSON.parse(JSON.stringify())4.5.cloneDeep() 五、结论 前言 在理…

C# 中的浅拷贝和深拷贝

在本文中&#xff0c;将通过示例讨论C&#xff03;中的浅拷贝和深拷贝。这是上一篇文章的续篇。因此&#xff0c;在继续本文之前&#xff0c;请阅读以前的文章&#xff0c;其中之前使用示例讨论了C&#xff03;中的原型设计模式。 什么是深拷贝和浅拷贝&#xff1f; 浅复制和…

JAVA 浅拷贝和深拷贝

拷贝 拷贝即对已有的数据创建一个副本&#xff0c;在 Java 中&#xff0c;拷贝可分为引用拷贝、浅拷贝、深拷贝。 引用拷贝 在 Java 中&#xff0c;实例化后的对象存储在堆区&#xff0c;而局部变量存放在局部变量表(栈)中&#xff0c;如&#xff1a; public void yinYongC…

js浅拷贝和深拷贝

1、JS数据类型 基本数据类型&#xff1a;Boolean、String、Number、null、undefined 引用数据类型&#xff1a;Object、Array、Function、RegExp、Date等 2、深拷贝与浅拷贝 深拷贝和浅拷贝都只针对引用数据类型&#xff0c; 浅拷贝会对对象逐个成员依次拷贝&#xff0c;但…

C++浅拷贝和深拷贝

1、浅拷贝 浅拷贝&#xff1a;又称值拷贝&#xff0c;将源对象的值拷贝到目标对象中去&#xff0c;本质上来说源对象和目标对象共用一份实体&#xff0c;只是所引用的变量名不同&#xff0c;地址其实还是相同的。 举个简单的例子&#xff0c;你的小名叫西西&#xff0c;大名叫…

彻底理解Python中浅拷贝和深拷贝的区别

目录 前言 1. 浅拷贝和深拷贝的概念 2. is和的区别 3. 赋值操作 4. copy模块里面的copy()方法 5. copy模块里面的deepcopy()方法 6.字典自带的copy方法 7.切片表达式拷贝 8.总结 前言 Python 的所有变量其实都是指向内存中的对象的一个指针&#xff0c;这确实和之前学…

如何理解java的回调函数?

对于技术问题&#xff0c;会用是一回事&#xff0c;理解这个技术问题的来龙去脉、设计者当初为什么要设计这个功能、这个技术问题有哪些优势、适用哪些场景又是另外回事了。 前者照猫画虎得其形&#xff0c;后者形神兼备得其意&#xff0c;这也是所谓青铜与王者的区别。 会使…

java使用回调函数

java回调函数 回调函数&#xff08;callback Function&#xff09;&#xff0c;顾名思义就是用来回调的函数。在两个类A、B中&#xff0c;A在调用B接口的同时B也在调用A 回调函数也常用于线程中的异步获取消息。 举个简单的例子&#xff0c;公司中老板分发任务给员工A&#…

java中回调函数的实现

在java的事务中&#xff0c;有时候可能会遇到以下情况&#xff0c;第一步是更新某某表&#xff0c;中间可能要更新不确定的多步&#xff0c;最后一步是更新缓存&#xff0c;结构大致如下&#xff1a; &#xff08;1&#xff09;updateA(); &#xff08;2&#xff09;updateXX…

什么是java回调函数

回调函数 一&#xff1a;故事背景二&#xff1a;概念三&#xff1a;回调函数的作用四&#xff1a;java中如何进行回调4.1 类图4.2 定义回调接口4.3 实现回调接口4.4 调用方法使用回调函数4.5 Main函数调用4.6 总结描述 五&#xff1a;回调函数的优点5.1 灵活性5.2 解耦性5.3 异…

简单举例JAVA回调函数的实现

来自维基百科的对回调&#xff08;Callback&#xff09;的解释&#xff1a;In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execut…

java回调函数机制

Java回调函数机制 参考了网上的一些资料&#xff0c;下面也做出一些总结&#xff0c;供初学者了解学习。 一、 概述 软件模块之间总是存在着一定的接口&#xff0c;从调用方式上&#xff0c;可以把他们分为三类&#xff1a;同步调用、回调、异步调用 。 同步调用&#xff1a;一…

java中如何实现回调函数

最近工作需要研究了一会别人写的库&#xff0c;其中充满着各种"回调函数"&#xff0c;因此把自己理解给记录下来&#xff0c;存档。 首先我们来看看回调函数 这个概念的具体由来&#xff0c;百度百科的示义如下&#xff1a; 回调函数就是一个通过函数指针调用的函数。…

Java回调函数 + 使用案例

文章目录 前言什么是回调函数第0个版本第1个版本第2个版本第3个版本第4个版本第5个版本第6个版本回头解析前言描述的问题1. MethodIntrospector.selectMethods()2. 抽象类MethodIntrospector3. 方法selectMethods()4. 成员变量USER_DECLARED_METHODS5. 方法doWithMethods()6. d…

Java-回调函数

什么是回调 函数调用可以分为三种模式&#xff0c;串行调用、异步调用、回调。这三种都是日常开发中常见到的方式。 一个方法执行完&#xff0c;再执行下一个&#xff0c;串行逻辑会阻塞线程执行流程&#xff0c;等到所有逻辑执行结束&#xff0c;线程才会结束。 异步执行是…

java回调函数(callBack)

最近有个新同事给我写了个接口&#xff0c;说是用到了回调&#xff0c;我等了半天发现结果才返回回来&#xff0c;把我都整急了。最后我看了他的代码&#xff0c;目瞪口呆。他还信誓旦旦的说没错&#xff0c;按网上的例子来写的。 我一搜&#xff0c;网上例子真一大堆&#xff…

java回调函数的作用以及运用

模块之间总是存在这一定的接口&#xff0c;从调用方式上看&#xff0c;可以分为三类&#xff1a;同步调用、回调和异步调用。同步调用是一种阻塞式调用&#xff0c;也是我们在写程序中经常使用的&#xff1b;回调是一种双向的调用模式&#xff0c;也就是说&#xff0c;被调用的…