赞
踩
前言:在日常的开发中,我们常常遇到一些我们不懂的知识,比如我最近看到一个Object.assign() ,就不太清楚其究竟代表着啥意思,因而在查阅资料后,得知其是前端浅拷贝的一种方式。以前也有听说过深拷贝,但不太清楚其代表的意思。因此,今天借此篇博文,来看看什么是浅拷贝,什么又是深拷贝,它们又是如何在开发中运用的。
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
为了便于对于浅拷贝和深拷贝的记忆,我们引入引用拷贝和对象拷贝来加深其理解:
引用拷贝:创建一个指向对象的引用变量的拷贝,具体代码如下:
- 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
结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
深拷贝和浅拷贝都是对象拷贝,其各有特点,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,因而相对开销较小;深拷贝把要复制的对象所引用的对象都复制了一遍,速度较慢并且花销较大。在实际的应用中,我们对于浅拷贝的方法用的较多,比如常见的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深拷贝和浅拷贝的区别
浅拷贝和深拷贝都是只针对于像Object,Array这样的复杂对象,经常用于前端js对象的封装和改变。其两者的区别在于——浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制,
浅拷贝就是增加了一个指针指向已存在的内存(JavaScript并没有指针的概念,这里只是用于辅助说明),浅拷贝只是拷贝了内存地址,子类的属性指向的是父类属性的内存地址,当子类的属性修改后,父类的属性也随之被修改。就如同:一件衣服两个人穿不管你穿还是我穿都还是同一件衣服。
深拷贝就是增加一个指针,并申请一个新的内存,并且让这个新增加的指针指向这个新的内存地址使用深拷贝,在释放内存的时候就不会像浅拷贝一样出现释放同一段内存的错误,当我们需要复制原对象但有不能修改原对象的时候,深拷贝就是一个,也是唯一的选择,就如同买不同的新衣服。像es6的新增方法都是深拷贝,所以推荐使用es6语法。
浅拷贝:
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));
- }
浅拷贝与深拷贝是一道经久不衰的面试题。
我们要答好这道题,就必须先理清其关系,简单来说,深拷贝可以视为浅拷贝+递归。
深拷贝、浅拷贝的理解与使用场景 - 简书 (jianshu.com)
https://blog.csdn.net/weixin_50964668/article/details/113922558
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。