【概述

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

B.a = A.a;
B.b = A.b;

拷贝对象是很常见的,主要是为了在新的上下文环境中复用现有对象的部分或全部数据。Java中的对象拷贝主要分为

  • 浅拷贝( Shallow Copy )

  • 深拷贝( Deep Copy )

Java中的数据类型分为基本数据类型引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。

【浅拷贝(Shallow Copy)

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。

  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

具体模型如下图所示,可以看到基本数据类型的成员变量,对其值创建了新的拷贝;而引用数据类型的成员变量的实例仍然是只有一份,两个对象的该成员变量都指向同一个实例。

浅拷贝的实现方式

  • 拷贝构造方法实现浅拷贝

拷贝构造方法指的是该类的构造方法参数为该类的对象。使用拷贝构造方法可以很好地完成浅拷贝,直接通过一个现有的对象创建出与该对象属性相同的新的对象。

  • 重写clone()方法进行浅拷贝

Object类是类结构的根类,其中有一个方法

protected Object clone() throws CloneNotSupportedException

这个方法就是进行的浅拷贝。有了这个浅拷贝模板,可以通过调用clone()方法来实现对象的浅拷贝。但是需要注意:

(1)Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以无法直接使用。

(2)使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。

对于这两点,我们的解决方法是:在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。

【深拷贝的实现方式】

首先介绍对象图的概念。设想一下,一个类有一个对象,其成员变量中又有一个对象,该对象指向另一个对象,另一个对象又指向另一个对象,直到一个确定的实例。这就形成了对象图。那么对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象图进行拷贝。

一句话,深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间;而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。深拷贝模型如下图所示,可以看到所有的成员变量都进行了复制。

因为创建内存空间和拷贝整个对象图,所以深拷贝相比于浅拷贝速度较慢并且花销较大。

  • 重写clone方法来实现深拷贝

与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说只要每一层的每个对象都进行浅拷贝,就等于实现了深拷贝。

@Override
public Object clone() {
//深拷贝
try {
// 直接调用父类的clone()方法
Student student = (Student) super.clone();
student.引用对象 = (引用对象) 引用对象.clone();
return student;
} catch (CloneNotSupportedException e) {
return null;
}
}
  • 对象序列化实现深拷贝

虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。

//将对象写入流中
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(拷贝对象); //从流中取出
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
return (拷贝对象)objectInputStream.readObject();
  • JSON或者XML方式实现深拷贝

因为一个POJO对象可以通过JSON库变成一个json字符串(通过XML库变成一个xml字符串),再通过对应的类库又反序列化成另外一个完整的对象。

String json =JSON.toJSONString(src);
T object = JSON.parseObject(json, clazz);

Over...

Java之浅拷贝和深拷贝的更多相关文章

  1. Java的浅拷贝与深拷贝

    Java的浅拷贝与深拷贝 Java中,所有的类都继承Object,Object中有clone方法,它被声明为了 protected ,所以我们但是如果要使用该方法就得重写且声明为public,必须在要 ...

  2. 浅析java的浅拷贝和深拷贝

    Java中任何实现了Cloneable接口的类都可以通过调用clone()方法来复制一份自身然后传给调用者.一般而言,clone()方法满足:       (1) 对任何的对象x,都有x.clone( ...

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

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

  4. 渐析java的浅拷贝和深拷贝

          首先来看看浅拷贝和深拷贝的定义:       浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝.       深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所 ...

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

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

  6. 初始JAVA中浅拷贝和深拷贝

    1. 简单变量的复制 public static void main(String[] args) { int a = 5; int b = a; System.out.println(a); Sys ...

  7. Java之浅拷贝与深拷贝

    ----?浅拷贝 --- 概念 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.简单说,浅拷贝就是只复制所考虑的对象,而不复制它所引用的对象 --- 实现方 ...

  8. java的浅拷贝和深拷贝(待解决)

    1.什么是浅拷贝,什么是深拷贝? 2.storm的并行度问题,需要使用全局变量static ConcorrentHashMap,因为加了static,所有的线程只能拷贝该全局变量的一个唯一的副本,进行 ...

  9. 【转】JAVA中的浅拷贝和深拷贝

    原文网址:http://blog.bd17kaka.net/blog/2013/06/25/java-deep-copy/ JAVA中的浅拷贝和深拷贝(shallow copy and deep co ...

随机推荐

  1. oracle绑定变量测试及性能对比

    1.创建测试数据 2.查看cursor_sharing的值 SQL> show parameter cursor_sharing; NAME TYPE VALUE --------------- ...

  2. Centos7 添加用户及设置权限

    一.添加用户 1.登录root 用户 [gau@localhost /]$ su Password: # 输入密码 [root@localhost /]# 2.添加用户 [root@localhost ...

  3. centos7制作U盘启动盘-九五小庞

    一.准备相关软件 1.8G以上U盘 2.UltraISO虚拟光驱(试用版即可)最新版 下载地址:https://cn.ultraiso.net/xiazai.html  点击下载试用 3.CentOS ...

  4. Mybatis plus 报错Invalid bound statement (not found) 终极解决办法

    我产生的错误原因是写的mapper继承BaseMapper没有添加泛型: 点进去: 为了解决这个bug,网上很多人也提出了解决办法:1.检查xml文件的namespace是否正确 2.Mapper.j ...

  5. Git:.gitignore和.gitkeep文件的使用 让空文件夹被跟踪

    Git:.gitignore和.gitkeep文件的使用 Git:.gitignore和.gitkeep文件的使用 https://majing.io/posts/10000001781172 .gi ...

  6. try-catch 异常捕获学习

    #include <iostream> #include <string> #include <vector> #include <stdexcept> ...

  7. 数据湖-Apache Hudi

    Hudi特性 数据湖处理非结构化数据.日志数据.结构化数据 支持较快upsert/delete, 可插入索引 Table Schema 小文件管理Compaction ACID语义保证,多版本保证 并 ...

  8. Phoenix表和索引分区优化方法

    Phoenix表和索引分区,基本优化方法 优化方法 1. SALT_BUCKETS RowKey SALT_BUCKETS 分区 2. Pre-split RowKey分区 3. 分列族 4. 使用压 ...

  9. MacOS Big Sur开HiDPI

    我自己的环境: 开启hidpi的目的是为了让显示更加细腻,代价是缩小了显示范围. 自己在网上看了很多帖子,也尝试了几种,有些方法已经不再适合Big Sur系统了,所以本文提供一种可用的,在Big Su ...

  10. linux开发各种I/O操作简析,以及select、poll、epoll机制的对比

    作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:羽林君 IO 概念区分 四个相关概念: 同步(Synchronous) 异步( Asynchronous) 阻塞( Blocking ) 非阻塞( ...