目录

  • 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 形式,否则会导致序列化失败。

基于java实现浅拷贝和深拷贝相关推荐

  1. Java的浅拷贝与深拷贝总结

    Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.举例说明:比如,对象A和对象B都属于类S,具有属性a和b.那么对对象A进行拷贝 ...

  2. java中浅拷贝和深拷贝_java中的浅拷贝和深拷贝

    复制 将一个对象的引用复制给另一个对象,一共有三种方式.第一种方式是直接赋值,第二种方式是浅复制,第三种方式是深复制. 1.直接赋值 在Java中,A a1 = a2,这实际上复制的是引用,也就是说 ...

  3. Java中浅拷贝与深拷贝之间的区别

    在深入探讨Java中浅表副本与深表副本之间的差异之前,让我们看看首先进行克隆的是什么. 什么是克隆? 克隆是在内存中创建现有对象的精确副本的过程.在Java中,java.lang.Object类的cl ...

  4. Java中浅拷贝和深拷贝的区别

    深拷贝和浅拷贝的区别 浅拷贝:被拷贝的对象的所有属性值都与原来的对象相同,而对象的所有属性引用仍然指向原来的属性所指向的内存地址.需要注意的是cloneObj == obj 返回的是false,所以使 ...

  5. Java【浅拷贝和深拷贝】之间的区别

    浅拷贝 浅拷贝定义定义 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.即对象的浅拷贝会对"主"对象进行拷贝,但不会复制主对象里面所引用 ...

  6. Java提高篇 —— Java浅拷贝和深拷贝

    一.前言 我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能 ...

  7. Java浅拷贝和深拷贝的方式

    文章目录 1. 前言 2. 概念介绍 2.1 拷贝 / 克隆的概念 2.2 为什么需要拷贝方法? 2.3 什么是浅拷贝?浅拷贝和深拷贝的区别是什么? 3. 深拷贝的实现方式 3.1 手动深拷贝 3.2 ...

  8. Java基础—复制之深拷贝与浅拷贝

    目录 一.浅拷贝(Shallow Copy) 二.深拷贝(Deep Copy) Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去 ...

  9. java深入理解浅拷贝和深拷贝

    文章目录 简介 拷贝接口 使用clone导致的浅拷贝 使用clone的深拷贝 不要overridden clone 总结 简介 拷贝对象是java中经常会遇到的问题.java中存在两种类型,基础类型和 ...

最新文章

  1. win10如何查看NVIDIA驱动的版本
  2. 使用ContentProvider
  3. SAP内部订单使用实例
  4. 在RHEL5/CentOS5上配置使用Open×××
  5. jquery 里 $(this)的用法
  6. 转:OAuth 2.0
  7. c swap方法在哪个库里面_IOT操作系统用C++库的经验总结
  8. 全球抵押销售点(POS)软件行业调研及趋势分析报告
  9. loacallhost:80被占用解决方法
  10. 解决Office2010每次打开都要配置进度的问题
  11. Java获取时间戳,System.currentTimeMillis() 和 System.nanoTime() 哪个更快?
  12. 井字游戏HTML,HTML5井字棋游戏
  13. 使用rufus制作windows系统安装u盘
  14. 加权算数平均大于等于几何平均
  15. Spring Boot中的配置文件使用以及重新加载
  16. 媒体实录:百度林元庆第一时间解读百度大脑VS.最强大脑第一场
  17. 用计算机弹百战成诗,百战成诗(80P纯女热血翻唱)
  18. 原根算法C语言,算法导论-----数论-----元素的幂
  19. 螺旋无限延伸_八卦中的双螺旋结构,无限大∞符号隐含的秘密
  20. 展望计算机体系结构的未来发展方向(学院作业)随便写的

热门文章

  1. MLR原理及deepctr组网实现MLR
  2. Java中OOA、OOD、OOP、OOT、OOSM、OOM的具体含义解释
  3. AutoFormR8冲压连续模具带料分析视频教程
  4. “慷慨的上帝” vs “吝啬的上帝”
  5. 解决Python安装alipay-sdk-python3.3.398遇到的错误
  6. 《死亡搁浅》叙事分析:点石成金的“迈达斯之触”
  7. 深耕城市治理场景,百度智能云联合慧联无限推内涝智能检测预警
  8. Postgresql - Cluster
  9. 「事件流处理架构」事件流处理的八个趋势
  10. 520用代码捕获女神芳心