Java之浅拷贝和深拷贝
【概述】
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之浅拷贝和深拷贝的更多相关文章
- Java的浅拷贝与深拷贝
Java的浅拷贝与深拷贝 Java中,所有的类都继承Object,Object中有clone方法,它被声明为了 protected ,所以我们但是如果要使用该方法就得重写且声明为public,必须在要 ...
- 浅析java的浅拷贝和深拷贝
Java中任何实现了Cloneable接口的类都可以通过调用clone()方法来复制一份自身然后传给调用者.一般而言,clone()方法满足: (1) 对任何的对象x,都有x.clone( ...
- Java的浅拷贝与深拷贝总结
Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.举例说明:比如,对象A和对象B都属于类S,具有属性a和b.那么对对象A进行拷贝 ...
- 渐析java的浅拷贝和深拷贝
首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所 ...
- Java中浅拷贝和深拷贝的区别
浅拷贝和深拷贝的定义: 浅拷贝: 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.即对象的浅拷贝会对"主"对象进行拷贝,但不会复制主对象 ...
- 初始JAVA中浅拷贝和深拷贝
1. 简单变量的复制 public static void main(String[] args) { int a = 5; int b = a; System.out.println(a); Sys ...
- Java之浅拷贝与深拷贝
----?浅拷贝 --- 概念 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.简单说,浅拷贝就是只复制所考虑的对象,而不复制它所引用的对象 --- 实现方 ...
- java的浅拷贝和深拷贝(待解决)
1.什么是浅拷贝,什么是深拷贝? 2.storm的并行度问题,需要使用全局变量static ConcorrentHashMap,因为加了static,所有的线程只能拷贝该全局变量的一个唯一的副本,进行 ...
- 【转】JAVA中的浅拷贝和深拷贝
原文网址:http://blog.bd17kaka.net/blog/2013/06/25/java-deep-copy/ JAVA中的浅拷贝和深拷贝(shallow copy and deep co ...
随机推荐
- 为什么[] == false 为true
首先要讲一下js的数据类型分为: 1.基本数据类型(原始数据类型):String.Boolean.Number.null.undefined.Symbol 2.引用数据类型:Object.Array. ...
- 浅谈JavaScript代码性能优化2
一.减少判断层级 从下图代码中可以明显看出,同样的效果判断层级的减少可以优化性能 二.减少作用域链查找层级 简单解释下,下图中第一个运行foo函数,bar函数内打印name,bar作用域内没有name ...
- Java中,那些关于String和字符串常量池你不得不知道的东西
老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...
- windows激活密钥
密钥来源,微软官方 KMS 客户端安装密钥 | Microsoft Docs Windows Server 2008 R2 操作系统版本 KMS 客户端安装程序密钥 Windows Server 20 ...
- jQuery 点击input框标题收起
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 提供个HDFS的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录 文件所在目录不存在则自动创建相应目录;删除目录时,由用户指定当该目录不为空时是否还删 除该目录
import java.io.IOException; import java.util.Scanner; import org.apache.hadoop.fs.*; public class G_ ...
- 广告召回 Query-Ad Matching
小结: 1.最为基础的召回链路就是要保证召回层的相关性,但是相关性高的广告并不一定具有很高的商业价值,所以开始尝试将一些商业化业务指标作为召回的依据 百度凤巢新一代广告召回系统--"莫比乌斯 ...
- 更新gitignore后如何使其生效
Files already tracked by Git are not affected; Git - gitignore Documentation https://git-scm.com/doc ...
- bzoj 2038(莫队算法)
2038: [2009国家集训队]小Z的袜子(hose) 时间限制: 20 Sec 内存限制: 259 MB 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来 ...
- Spring框架相关博文集
收藏一些干货博文. Spring 多数据源管理源码分析 Spring事务管理详解 Spring源码解析 Spring框架自学之路