前言:在日常的开发中,我们常常遇到一些我们不懂的知识,比如我最近看到一个Object.assign() ,就不太清楚其究竟代表着啥意思,因而在查阅资料后,得知其是前端浅拷贝的一种方式。以前也有听说过深拷贝,但不太清楚其代表的意思。因此,今天借此篇博文,来看看什么是浅拷贝,什么又是深拷贝,它们又是如何在开发中运用的。

1,浅拷贝和深拷贝的概念

1.1 浅拷贝和深拷贝

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。

为了便于对于浅拷贝和深拷贝的记忆,我们引入引用拷贝和对象拷贝来加深其理解:

1.2 引用拷贝和对象拷贝

引用拷贝:创建一个指向对象的引用变量的拷贝,具体代码如下:

public class QuoteCopy {public static void main(String[] args) {Teacher teacher = new Teacher("riemann", 28);Teacher otherTeacher = teacher;System.out.println(teacher);System.out.println(otherTeacher);}
}class Teacher {private String name;private int age;public Teacher(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

输出结果:

com.test.Teacher@28a418fc
com.test.Teacher@28a418fc

结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherTeacher的只是引用而已,他们都指向了一个相同的对象Teacher(“riemann”,28)。 这就叫做引用拷贝。

对象拷贝:创建对象本身的一个副本,代码如下:

public class ObjectCopy {public static void main(String[] args) throws CloneNotSupportedException {Teacher teacher = new Teacher("riemann", 28);Teacher otherTeacher = (Teacher) teacher.clone();System.out.println(teacher);System.out.println(otherTeacher);}
}class Teacher implements Cloneable {private String name;private int age;public Teacher(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Object clone() throws CloneNotSupportedException {Object object = super.clone();return object;}
}

输出结果:

com.test.Teacher@28a418fc
com.test.Teacher@5305068a

结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。

1.3 浅拷贝和深拷贝的实例

深拷贝和浅拷贝都是对象拷贝,其各有特点,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,因而相对开销较小;深拷贝把要复制的对象所引用的对象都复制了一遍,速度较慢并且花销较大。在实际的应用中,我们对于浅拷贝的方法用的较多,比如常见的clone()方法,其拷贝出来的对象是通过浅拷贝,若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝,具体代码示例如下:

我们可以通过创建一个学生对象,里边引入教授来区别浅拷贝和深拷贝:

//浅拷贝内部对象——教授
class Professor0 implements Cloneable {String name;int age;Professor0(String name, int age) {this.name = name;this.age = age;}public Object clone() throws CloneNotSupportedException {return super.clone();}
}//浅拷贝克隆和修改对象——学生
class Student0 implements Cloneable {String name;// 常量对象。int age;Professor0 p;// 学生1和学生2的引用值都是一样的。Student0(String name, int age, Professor0 p) {this.name = name;this.age = age;this.p = p;}public Object clone() {Student0 o = null;try {o = (Student0) super.clone();} catch (CloneNotSupportedException e) {System.out.println(e.toString());}return o;}
}

通过固定方法调用查看区别:

public class ShallowCopy {public static void main(String[] args) {Professor0 p = new Professor0("wangwu", 50);Student0 s1 = new Student0("zhangsan", 18, p);Student0 s2 = (Student0) s1.clone();s2.p.name = "lisi";s2.p.age = 30;s2.name = "z";s2.age = 45;System.out.println("学生s1的姓名:" + s1.name +"\n学生s1教授的姓名:" + s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age);// 学生1的教授}
}结果:s2变了,但s1也变了
证明:s1的p和s2的p指向的是同一个对象。

这在我们有的实际需求中,却不是这样,因而我们需要深拷贝:

//深拷贝内部对象——教授
class Professor implements Cloneable {String name;int age;Professor(String name, int age) {this.name = name;this.age = age;}public Object clone() {Object o = null;try {o = super.clone();} catch (CloneNotSupportedException e) {System.out.println(e.toString());}return o;}
}
//浅拷贝克隆对象——学生
class Student implements Cloneable {String name;int age;Professor p;Student(String name, int age, Professor p) {this.name = name;this.age = age;this.p = p;}public Object clone() {Student o = null;try {o = (Student) super.clone();} catch (CloneNotSupportedException e) {System.out.println(e.toString());}o.p = (Professor) p.clone();return o;}
}

深拷贝方法修改对象:

public class DeepCopy {public static void main(String args[]) {long t1 = System.currentTimeMillis();Professor p = new Professor("wangwu", 50);Student s1 = new Student("zhangsan", 18, p);Student s2 = (Student) s1.clone();s2.p.name = "lisi";s2.p.age = 30;System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age);// 学生1的教授不改变。long t2 = System.currentTimeMillis();System.out.println(t2-t1);}
}

当然我们还有一种深拷贝方法,就是将对象串行化:

import java.io.*;class Professor2 implements Serializable {private static final long serialVersionUID = 1L;String name;int age;Professor2(String name, int age) {this.name = name;this.age = age;}
}class Student2 implements Serializable {private static final long serialVersionUID = 1L;String name;// 常量对象。int age;Professor2 p;// 学生1和学生2的引用值都是一样的。Student2(String name, int age, Professor2 p) {this.name = name;this.age = age;this.p = p;}public Object deepClone() throws IOException, OptionalDataException,ClassNotFoundException {// 将对象写到流里ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(this);// 从流里读出来ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);return (oi.readObject());}
}public class DeepCopy2 {public static void main(String[] args) throws OptionalDataException,IOException, ClassNotFoundException {long t1 = System.currentTimeMillis();Professor2 p = new Professor2("wangwu", 50);Student2 s1 = new Student2("zhangsan", 18, p);Student2 s2 = (Student2) s1.deepClone();s2.p.name = "lisi";s2.p.age = 30;System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); // 学生1的教授不改变。long t2 = System.currentTimeMillis();System.out.println(t2-t1);}
}

参考资料:(144条消息) Java深入理解深拷贝和浅拷贝区别_老周聊架构的博客-CSDN博客_java深拷贝和浅拷贝的区别

2.浅拷贝和深拷贝的适用场景

2.1 浅拷贝和深拷贝的前端应用

浅拷贝和深拷贝都是只针对于像Object,Array这样的复杂对象,经常用于前端js对象的封装和改变。其两者的区别在于——浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制,

浅拷贝就是增加了一个指针指向已存在的内存(JavaScript并没有指针的概念,这里只是用于辅助说明),浅拷贝只是拷贝了内存地址,子类的属性指向的是父类属性的内存地址,当子类的属性修改后,父类的属性也随之被修改。就如同:一件衣服两个人穿不管你穿还是我穿都还是同一件衣服。

深拷贝就是增加一个指针,并申请一个新的内存,并且让这个新增加的指针指向这个新的内存地址使用深拷贝,在释放内存的时候就不会像浅拷贝一样出现释放同一段内存的错误,当我们需要复制原对象但有不能修改原对象的时候,深拷贝就是一个,也是唯一的选择,就如同买不同的新衣服。像es6的新增方法都是深拷贝,所以推荐使用es6语法。

2.2浅拷贝和深拷贝的使用实例

浅拷贝:

1.Object.assign:Object.assign 是 Object 的一个方法,该方法可以用于 JS 对象的合并等多个用途,其中一个用途就是可以进行浅拷贝。该方法的第一个参数是拷贝的目标对象,后面的参数是拷贝的来源对象(也可以是多个来源)。

2、扩展运算符:使用扩展运算符也可以完成浅拷贝

3、Array.prototype.concat:数组的 concat 方法其实也是浅拷贝,使用场景比较少,使用concat连接一个含有引用类型的数组时,需要注意修改原数组中的元素的属性,因为它会影响拷贝之后连接的数组

4、Array.prototype.slice

数组的 slice 方法其实也是浅拷贝,使用场景比较少,同cancat

5、使用第三方库&手动实现

社区存在一些优秀工具函数库,在开发过程中可以直接利用这些库暴露的函数直接实现浅拷贝,比如lodash就提供了clone方法供用户进行浅拷贝

深拷贝:

1.可以通过 for in 实现。

function deepCopy1(obj) {let o = {}for(let key in obj) {o[key] = obj[key]}return o
}let obj = {a:1,b: undefined,c:function() {},
}
console.log(deepCopy1(obj))

2. 可以借用JSON对象的parse和stringify(对象的深拷贝)

function deepClone(obj){let _obj = JSON.stringify(obj),objClone = JSON.parse(_obj);return objClone
}
let a=[0,1,[2,3],4],b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

3.递归 自身调用自身(对象的深拷贝)

function deepClone1(obj) {//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝var objClone = Array.isArray(obj) ? [] : {};//进行深拷贝的不能为空,并且是对象或者是if (obj && typeof obj === "object") {for (key in obj) {if (obj.hasOwnProperty(key)) {if (obj[key] && typeof obj[key] === "object") {objClone[key] = deepClone1(obj[key]);} else {objClone[key] = obj[key];}}}}return objClone;
}

4.concat(数组的深拷贝)

使用concat合并数组,会返回一个新的数组。

5.有些同学可能见过用系统自带的JSON来做深拷贝的例子,下面来看下代码实现

function cloneJSON(source) {return JSON.parse(JSON.stringify(source));
}

2.3 浅拷贝与深拷贝的关系(待更新)

浅拷贝与深拷贝是一道经久不衰的面试题。

我们要答好这道题,就必须先理清其关系,简单来说,深拷贝可以视为浅拷贝+递归。

深拷贝、浅拷贝的理解与使用场景 - 简书 (jianshu.com)

https://blog.csdn.net/weixin_50964668/article/details/113922558

浅拷贝和深拷贝的基本含义和应用场景相关推荐

  1. python 实例对象 浅拷贝_python的浅拷贝和深拷贝

    一.浅拷贝和深拷贝的作用简述 浅拷贝和深拷贝可以运用于不同的场景,比如一个夫妻联名账户,将账户的信息都放入一个对象中,夫妻两人的账户信息都通过这个对象拷贝而来(两人一人一个账号),但是任何一个人取出存 ...

  2. js之浅拷贝和深拷贝

    js数据类型主要分基本数据类型和引用数据类型.前者包括Number,String等,后者主要是Object,因此以下会针对不同的数据类型来分析,需要的朋友可以参考一下 1.js内存 js内存,或者说大 ...

  3. python深浅拷贝 面试_[面试题二]百度资深面试官:python赋值、浅拷贝与深拷贝

    内存管理相关的面试一直是面试中的重点考察内容.赋值.深拷贝与浅拷贝是日常编码工作中,需要时常关注的知识点.不小心可是会写出bug哦- 赋值 先来看一段代码,下面两段代码的输出结果会是什么呢? # ca ...

  4. OpenCV学习笔记(六)——感兴趣区域ROI(浅拷贝与深拷贝的区别)

    目录 1 浅拷贝 2 深拷贝 示例代码 有时我们只对一张图像中的部分区域感兴趣,而原图像又比较大,如果带着非感兴趣区域一起处理会占用大量的内存,因次我们希望从原图像中截取部分图像后再进行处理.我们将这 ...

  5. Java中的浅拷贝与深拷贝

    一.引用拷贝与对象拷贝 class Person implements Cloneable{private String name;private int age;...省略get和set方法 pro ...

  6. 43 JavaScript中的浅拷贝与深拷贝

    技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1.浅拷贝与深拷贝 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用. 深拷贝拷贝多层,每 ...

  7. 【进阶4-1期】详细解析赋值、浅拷贝和深拷贝的区别

    一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为下面 2 部分 基本数据类型:赋值,赋值之后两个变量互不影响 引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有 ...

  8. C# 之String以及浅拷贝与深拷贝

    C# 之String以及浅拷贝与深拷贝  一.String到底是值类型还是引用类型 MSDN 中明确指出 String 是引用类型而不是值类型,但 String 表面上用起来却像是值类型,这又是什么原 ...

  9. C# 浅拷贝与深拷贝区别 解惑篇

    问题起源: 昨天被同事问到一个浅拷贝与深拷贝区别的问题,说实在的,记得在学校时在书在看过相关概念区别. 只是,那时的在校生,又有几个能对书本上那写的尽量让鬼都看不懂知识能清晰的理解呢. 工作后虽然也有 ...

最新文章

  1. java的知识点27——lambda推导
  2. phoenix hbase Can't get master address from ZooKeeper; znode data == null
  3. mysql 4字节utf8_MySQL 4字节utf8字符更新失败一例
  4. sum()转字符串_Python字符串与内置函数
  5. 教育部计算机考研大纲,2018考研大纲从哪里看?
  6. 格式化显示(日期\货币)
  7. 第十章 动态选录协议
  8. 蓝桥杯2017年第八届C/C++省赛A组第一题-迷宫
  9. php imagick 取得psd缩略图,PHP中使用Imagick操作PSD文件实例
  10. Windows下Pidgin介绍/安装配置图文攻略
  11. MLIR再深入 —— CodeGen 总结
  12. 如何解决服务器证书不受信任,pycharm 如何跳出服务器证书不受信任的提示
  13. oracle 推进scn号
  14. 什么是上下变频器?以及对5G应用的作用
  15. centos 7 安装oxidized 实现交换机自动备份
  16. 吹爆CSDN插件助手的细节功能(保姆级图文)
  17. Mac raw数码照片处理SILKYPIX Pro9安装和激活教程
  18. linux 取消utc时间,ubuntu系统下禁用utc时间的设置方法
  19. 自动化运维(使用api自动化管理f5设备)
  20. 广告算法工程师、专家、leader

热门文章

  1. R语言机器学习mlr3:数据预处理和pipelines
  2. MaxScript通过Ole操作Phtoshop的范例,将几张图按图层合并为psd
  3. JetBrains PyCharm 配置pylint(Python代码审阅工具)教程
  4. Linux删除.nfsxxx文件
  5. 一文搞懂redis的存储机制AOF与RDB
  6. RationalDMIS 7.1 模型处理 (CAD模型分层 /CAD模型着色)
  7. mysql下载安装(清华源)
  8. CUDA笔记--GPU的结构与SM(流处理器)结构
  9. vue给按钮添加防抖
  10. Redis集群搭建(单机集群)