深拷贝和浅拷贝


值类型 vs 引用类型

在Java中,像数组、类Class、枚举Enum、Integer包装类等等,就是典型的引用类型,所以操作时一般来说采用的也是引用传递的方式;
但是Java的语言级基础数据类型,诸如int这些基本类型,操作时一般采取的则是值传递的方式,所以有时候也称它为值类型。
为了便于下文的讲述和举例,我们这里先定义两个类:Student和Major,分别表示「学生」以及「所学的专业」,二者是包含关系:
 
 // 学生的所学专业
public class Major {
private String majorName;
// 专业名称
private long majorId; // 专业代号
// ... 其他省略 ...
}
 
 // 学生
public class Student {
private String name; // 姓名
private int age; // 年龄
private Major major; // 所学专业
// ... 其他省略 ...
}
 

赋值 vs 浅拷贝 vs 深拷贝

对象赋值

赋值是日常编程过程中最常见的操作,最简单的比如:
 
 Student codeSheep = new Student();
Student codePig = codeSheep;
 
严格来说,这种不能算是对象拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象:
 
 

浅拷贝

浅拷贝属于对象克隆方式的一种,重要的特性体现在这个 「浅」 字上。
比如我们试图通过studen1实例,拷贝得到student2,如果是浅拷贝这种方式,大致模型可以示意成如下所示的样子:
 
 
 
很明显,值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。
 

深拷贝

深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本,就像这个样子:
 
 
 
原理很清楚明了,下面来看看具体的代码实现吧。
 

浅拷贝代码实现

还以上文的例子来讲,我想通过student1拷贝得到student2,浅拷贝的典型实现方式是:让被复制对象的类实现Cloneable接口,并重写clone()方法即可。
以上面的Student类拷贝为例:
 public class Student implements Cloneable {
private String name; // 姓名
private int age; // 年龄
private Major major; // 所学专业
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// ... 其他省略 ...
}
然后我们写个测试代码,一试便知:
 public class Test {    public static void main(String[] args) throws CloneNotSupportedException
{ Major m = new Major("计算机科学与技术",666666);
Student student1 = new Student("CodeSheep",18, m );
// 由 student1 拷贝得到 student2
Student student2 = (Student) student1.clone(); System.out.println( student1 == student2 );
System.out.println( student1 );
System.out.println( student2 );
System.out.println( "\n");
// 修改student1的值类型字段
student1.setAge( 35);
// 修改student1的引用类型字段
m.setMajorName( "电子信息工程");
m.setMajorId( 888888);
System.out.println( student1 );
System.out.println( student2 );
}
}
 
运行得到如下结果:
 
从结果可以看出:
  • student1==student2打印false,说明clone()方法的确克隆出了一个新对象;
  • 修改值类型字段并不影响克隆出来的新对象,符合预期;
  • 而修改了student1内部的引用对象,克隆对象student2也受到了波及,说明内部还是关联在一起的

深拷贝代码实现

1、深度遍历式拷贝

虽然clone()方法可以完成对象的拷贝工作,但是注意:clone()方法默认是浅拷贝行为,就像上面的例子一样。若想实现深拷贝需覆写 clone()方法实现引用对象的深度遍历式拷贝,进行地毯式搜索。
所以对于上面的例子,如果想实现深拷贝,首先需要对更深一层次的引用类Major做改造,让其也实现Cloneable接口并重写clone()方法:
 public class Major implements Cloneable {    

      @Override protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}
// ... 其他省略 ...
}
 
其次我们还需要在顶层的调用类中重写clone方法,来调用引用类型字段的clone()方法实现深度拷贝,对应到本文那就是Student类:
 public class Student implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException
{
Student student = (Student) super.clone();
student.major = (Major) major.clone(); // 重要!!!
return student;
}
// ... 其他省略 ...
}
 
这时候上面的测试用例不变,运行可得结果:
 
很明显,这时候student1和student2两个对象就完全独立了,不受互相的干扰。

2、利用反序列化实现深拷贝

用反序列化技术,我们也可以从一个对象深拷贝出另一个复制对象,而且这货在解决多层套娃式的深拷贝问题时效果出奇的好。
所以我们这里改造一下Student类,让其clone()方法通过序列化和反序列化的方式来生成一个原对象的深拷贝副本:
 public class Student implements Serializable {
private String name; // 姓名
private int age; // 年龄
private Major major; // 所学专业
public Student clone() {
try {
// 将对象本身序列化到字节流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream );
objectOutputStream.writeObject( this);
// 再将字节流通过反序列化方式得到对象副本
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
return (Student) objectInputStream.readObject();
} catch(IOException e) {
e.printStackTrace();
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
// ... 其他省略 ...
}
 
当然这种情况下要求被引用的子类(比如这里的Major类)也必须是可以序列化的,即实现了Serializable接口:
 public class Major implements Serializable {
// ... 其他省略 ...
}
 
这时候测试用例完全不变,直接运行,也可以得到如下结果:
 
很明显,这时候student1和student2两个对象也是完全独立的,不受互相的干扰,深拷贝完成。

Java拷贝——深拷贝与浅拷贝的更多相关文章

  1. Java的深拷贝和浅拷贝

    关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用 ...

  2. 细说 Java 的深拷贝和浅拷贝

    版权声明: 本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有. 未经允许,不得转载. 一.前言 任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外.在对一个现 ...

  3. 浅析java的深拷贝与浅拷贝

    (转自:http://www.cnblogs.com/chenssy/p/3308489.html) 首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式 ...

  4. Java实现深拷贝和浅拷贝

    1.类实现Cloneable才可以进行对象拷贝 2.Cloneable只实现浅拷贝,需要实现深拷贝的必须要重写clone()方法 3.利用反序列化也可以实现深拷贝,但是反序列化耗时较长 n.浅拷贝是指 ...

  5. 【Java】深拷贝和浅拷贝

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

  6. Python对象拷贝——深拷贝与浅拷贝

    对象赋值 浅拷贝 深拷贝 1. 对象赋值 对象的赋值实际上是对对象的引用.也就是说当把一个对象赋值给另一个对象时,只是拷贝了引用.如: >>> t1 = tuple('furzoom ...

  7. java基础——深拷贝和浅拷贝的区别

    浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝. 深拷贝:对基本数据类型进行值传递,对引用数据类型船舰一个新的对象,并复制内容,这是深拷贝.

  8. Java问题解读系列之IO相关---Java深拷贝和浅拷贝

    前几天和棒棒童鞋讨论Java(TA学的是C++)的时候,他提到一个浅拷贝和深拷贝的问题,当时的我一脸懵圈,感觉自己学Java居然不知道这个知识点,于是今天研究了一番Java中的浅拷贝和深拷贝,下面来做 ...

  9. java 深拷贝与浅拷贝

    yls 2019年11月07日 拷贝分为引用拷贝和对象拷贝 深拷贝和浅拷贝都属于对象拷贝 浅拷贝:通过Object默认的clone方法实现 实现Cloneable接口 public class She ...

随机推荐

  1. vmware15虚拟机安装教程

    自己碰到的问题:本人win7 64位旗舰版系统.之前用VMware12pro版本的软件,在安装Ubuntu18.04之后,有时候开启Ubuntu虚拟机时有点问题,重启就可以了.但是不稳定,所以改用VM ...

  2. Python多线程-ThreadLocal

    参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017630786314240#0 ThreadLocal ThreadLocal变量虽然是 ...

  3. QString字符串的查找与截取实例

    QString是Qt中封装的字符串类,相对于标准库里的string,使用方法有些不同,个人感觉使用qt习惯后,感觉QString更好用,下面的代码主要是针对QString的字符查找.截取做的测试: # ...

  4. 005_针对于go语言中速率限制的思考

    在之前的go语言的速率限制这篇文章里,我们尝试了普通的速率限制,和脉冲型速率限制.其中,脉冲型速率限制是放开了限制,里面有3个请求是一次性到达,然后再按照200ms的速度限制的,之前的代码如下所示: ...

  5. 用Python一键生成炫酷九宫格图片,火了朋友圈

  6. 【Canal】数据同步的终极解决方案,阿里巴巴开源的Canal框架当之无愧!!

    写在前面 在当今互联网行业,尤其是现在分布式.微服务开发环境下,为了提高搜索效率,以及搜索的精准度,会大量使用Redis.Memcached等NoSQL数据库,也会使用大量的Solr.Elastics ...

  7. Zookeeper学习(一)

    一.Zookeeper理解与选举机制 ①Zookeeper理解 概念:Zookeeper 是一个开源的分布式协调服务框架 ,主要用来解决分布式集群中应用系统的一致性问题和数据管理问题 特点:Zooke ...

  8. Q#–一个新年愿望清单

    本文章为机器翻译.https://blogs.msdn.microsoft.com/visualstudio/2018/12/24/qsharp-wish-list-for-new-year/# 在以 ...

  9. C#LeetCode刷题-分治算法

    分治算法篇 # 题名 刷题 通过率 难度 4 两个排序数组的中位数 C#LeetCode刷题之#4-两个排序数组的中位数(Median of Two Sorted Arrays)-该题未达最优解 30 ...

  10. C#LeetCode刷题之#448-找到所有数组中消失的数字(Find All Numbers Disappeared in an Array)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3712 访问. 给定一个范围在  1 ≤ a[i] ≤ n ( n ...