精华内容
下载资源
问答
  • Python深拷贝和浅拷贝使用方法发布时间:2020-06-06 16:52:01来源:亿速云阅读:182这篇文章运用了实例代码展示Python深拷贝和浅拷贝使用方法,代码非常详细,可供感兴趣的小伙伴们参考借鉴,希望对大家有所帮助。...

    Python深拷贝和浅拷贝使用方法

    发布时间:2020-06-06 16:52:01

    来源:亿速云

    阅读:182

    这篇文章运用了实例代码展示Python深拷贝和浅拷贝使用方法,代码非常详细,可供感兴趣的小伙伴们参考借鉴,希望对大家有所帮助。

    一、浅拷贝

    所谓浅拷贝,指的是对于某个对象,虽然创建了与该对象具有相同值的另一个对象,但是,这两个对象內部嵌套的对应子对象全都是同一个对象。简单地说,外部进行了拷贝,内部没有拷贝。

    以下方式得到的拷贝都是浅拷贝:

    ● 切片操作[:]

    ● 调用列表、字典、集合的方法copy()

    ● 调用内置函数List()、dict()、set(4.

    ● 调用标准库模块copy中的函数copy()import copy  # 导入标准库模块copy

    L1 = [1,[1,2,3],6]

    L2 = L1.copy() # [1, [1, 2, 3], 6] 使用list.copy()

    L2 = L1[:] # [1, [1, 2, 3], 6] # 使用索引切片的方式

    L2 = list(L1) #  [1, 2, 3], 6] # 使用list()函数赋值

    L2 = copy.copy(L1) # [1, [1, 2, 3], 6]  # 调用标准库模块copy中的函数copy()

    # 通过打印L1和L2的id可以看出,L2只拷贝了L1的外部,形成了一个和L1具有相同值的对象

    # L1和L2内部值的id全都相同,即引用的同一内存地址

    print('L1_id:%d' % id(L1)) # L1_id:140024932419056

    print('L2_id:%d' % id(L2)) # L2_id:140024932419456

    print('L1[1]_id:%d' % id(L1[1])) # L1[1]_id:140024932419376

    print('L2[1]_id:%d' % id(L2[1])) # L2[1]_id:140024932419376

    print('id_L1[2] %d' % id(L1[2])) # id_L1[2] 9466624

    print('id_L2[2] %d' % id(L2[2])) # id_L2[2] 9466624

    # 浅拷贝,对于列表内部嵌套的可变类型对象,修改L1[1][1]值,L2[1][1]值也会跟着改变

    # 实际上他们内部都引用着同一个内存id

    L1[1][1] = 5

    print(L1) # [1, [1, 5, 3], 6]

    print(L2) # [1, [1, 5, 3], 6]

    print('L1[1]_id:%d' % id(L1[1])) # L1[1]_id:140024932419376

    print('L2[1]_id:%d' % id(L2[1])) # L2[1]_id:140024932419376

    # 浅拷贝,对于列表内部的不可变类型对象,修改L1[2],

    # 因为是不可变类型,那么会重新调用一个值给予引用,L2[2]因此不受影响

    L1[2] = 8

    print(L1) # [1, [1, 5, 3], 8]

    print(L2) # [1, [1, 5, 3], 6]

    print('id_L1[2] %d' % id(L1[2])) # id_L1[2] 9466688

    print('id_L2[2] %d' % id(L2[2])) # id_L2[2] 9466624

    二、深拷贝

    所谓深拷贝,指的是:对于某个对象,创建与该对象具有相同值的另一个对象,同时,这两个对象内部嵌套的对应可变子对象全都不是同一个对象。简单地说,外部和内部都进行了拷贝。

    深拷贝的方法:

    ● 调用标准库模块copy中的函数deepcopy()import copy  # 导入标准库模块copy

    L1 = [1,[1,2,3],6]

    L2 = copy.deepcopy(L1) # [1, [1, 2, 3], 6]

    # 通过打印L1和L2的内存地址可以看出,其外部进行拷贝,L2是和L1具有相同值的新对象

    # 对于内部嵌套的可变类型对象,L1[1]和L2[1]内存地址并不相同

    # 对于内部嵌套的不可变类型对象,L1[2]和L2[2]内存地址相同,引用的同一内存地址

    print('L1_id:%d' % id(L1)) # L1_id:139984573203792

    print('L2_id:%d' % id(L2)) # L2_id:139984573203952

    print('L1[1]_id:%d' % id(L1[1])) # L1[1]_id:139984573203472

    print('L2[1]_id:%d' % id(L2[1])) # L2[1]_id:139984573204512

    print('id_L1[2] %d' % id(L1[2])) # id_L1[2] 9466624

    print('id_L2[2] %d' % id(L2[2])) # id_L2[2] 9466624

    # 深拷贝,列表内部嵌套的可变类型对象,修改L1[1][1] 为5不影响L2[1][1]的值,

    # 深拷贝是将L1和L2内部可变类型对象的值引用的内存地址分开来

    L1[1][1] = 5

    print(L1) # [1, [1, 5, 3], 6]

    print(L2) # [1, [1, 2, 3], 6]

    print('L1[1]_id:%d' % id(L1[1])) # L1[1]_id:139984573203472

    print('L2[1]_id:%d' % id(L2[1])) # L2[1]_id:139984573204512

    # 深拷贝,对于列表内部不可变类型对象,修改L1[2] = 8,因为是不可变类型,所以将L1[2]重新赋值引用,不影响L2[2]

    L1[2] = 8

    print(L1) # [1, [1, 5, 3], 8]

    print(L2) # [1, [1, 2, 3], 6]

    print('id_L1[2] %d' % id(L1[2])) # id_L1[2] 9466688

    print('id_L2[2] %d' % id(L2[2])) # id_L2[2] 9466624

    如果你能读到这里,恭喜你已经对Python深拷贝和浅拷贝有了从实践层面最深刻的体会了。如果想阅读更多相关内容的文章,欢迎关注亿速云行业资讯频道!

    展开全文
  • Java的深拷贝和浅拷贝

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

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

    1、创建对象的5种方式

    ①、通过 new 关键字

    这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象。比如 Object obj = new Object();

    ②、通过 Class 类的 newInstance() 方法

    这种默认是调用类的无参构造方法创建对象。比如 Person p2 = (Person) Class.forName("com.ys.test.Person").newInstance();

    ③、通过 Constructor 类的 newInstance 方法

    这和第二种方法类时,都是通过反射来实现。通过 java.lang.relect.Constructor 类的 newInstance() 方法指定某个构造器来创建对象。

    Person p3 = (Person) Person.class.getConstructors()[0].newInstance();

    实际上第二种方法利用 Class 的 newInstance() 方法创建对象,其内部调用还是 Constructor 的 newInstance() 方法。

    ④、利用 Clone 方法

    Clone 是 Object 类中的一个方法,通过 对象A.clone() 方法会创建一个内容和对象 A 一模一样的对象 B,clone 克隆,顾名思义就是创建一个一模一样的对象出来。

    Person p4 = (Person) p3.clone();

    ⑤、反序列化

    序列化是把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。

    具体如何实现可以参考我 这篇博文。

    3、Clone 方法

    本篇博客我们讲解的是 Java 的深拷贝和浅拷贝,其实现方式正是通过调用 Object 类的 clone() 方法来完成。在 Object.class 类中,源码为:

    protected native Object clone() throws CloneNotSupportedException;

    这是一个用 native 关键字修饰的方法,关于native关键字有一篇博客专门有介绍,不理解也没关系,只需要知道用 native 修饰的方法就是告诉操作系统,这个方法我不实现了,让操作系统去实现。具体怎么实现我们不需要了解,只需要知道 clone方法的作用就是复制对象,产生一个新的对象。那么这个新的对象和原对象是什么关系呢?

    4、基本类型和引用类型

    这里再给大家普及一个概念,在 Java 中基本类型和引用类型的区别。

    在 Java 中数据类型可以分为两大类:基本类型和引用类型。

    基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、short、int、long、float、double。

    引用类型则包括类、接口、数组、枚举等。

    Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆中,通过栈中的引用指向堆中存放的数据。

    48dc136630fae3001fd7409abb7e8952.png

    上图定义的 a 和 b 都是基本类型,其值是直接存放在栈中的;而 c 和 d 是 String 声明的,这是一个引用类型,引用地址是存放在 栈中,然后指向堆的内存空间。

    下面 d = c;这条语句表示将 c 的引用赋值给 d,那么 c 和 d 将指向同一块堆内存空间。

    5、浅拷贝

    我们看如下这段代码:

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    packagecom.ys.test;public class Person implementsCloneable{publicString pname;public intpage;publicAddress address;publicPerson() {}public Person(String pname,intpage){this.pname =pname;this.page =page;this.address = newAddress();

    }

    @Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone();

    }public voidsetAddress(String provices,String city ){

    address.setAddress(provices, city);

    }public voiddisplay(String name){

    System.out.println(name+":"+"pname=" + pname + ", page=" + page +","+address);

    }publicString getPname() {returnpname;

    }public voidsetPname(String pname) {this.pname =pname;

    }public intgetPage() {returnpage;

    }public void setPage(intpage) {this.page =page;

    }

    }

    View Code

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    packagecom.ys.test;public classAddress {privateString provices;privateString city;public voidsetAddress(String provices,String city){this.provices =provices;this.city =city;

    }

    @OverridepublicString toString() {return "Address [provices=" + provices + ", city=" + city + "]";

    }

    }

    View Code

    这是一个我们要进行赋值的原始类 Person。下面我们产生一个 Person 对象,并调用其 clone 方法复制一个新的对象。

    注意:调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。

    测试:

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    @Testpublic void testShallowClone() throwsException{

    Person p1= new Person("zhangsan",21);

    p1.setAddress("湖北省", "武汉市");

    Person p2=(Person) p1.clone();

    System.out.println("p1:"+p1);

    System.out.println("p1.getPname:"+p1.getPname().hashCode());

    System.out.println("p2:"+p2);

    System.out.println("p2.getPname:"+p2.getPname().hashCode());

    p1.display("p1");

    p2.display("p2");

    p2.setAddress("湖北省", "荆州市");

    System.out.println("将复制之后的对象地址修改:");

    p1.display("p1");

    p2.display("p2");

    }

    View Code

    打印结果为:

    ee327dac39ee2ae09f34ad4a4224e0e9.png

    首先看原始类 Person 实现 Cloneable 接口,并且覆写 clone 方法,它还有三个属性,一个引用类型 String定义的 pname,一个基本类型 int定义的 page,还有一个引用类型 Address ,这是一个自定义类,这个类也包含两个属性 pprovices 和 city 。

    接着看测试内容,首先我们创建一个Person 类的对象 p1,其pname 为zhangsan,page为21,地址类 Address 两个属性为 湖北省和武汉市。接着我们调用 clone() 方法复制另一个对象 p2,接着打印这两个对象的内容。

    从第 1 行和第 3 行打印结果:

    p1:com.ys.test.Person@349319f9

    p2:com.ys.test.Person@258e4566

    可以看出这是两个不同的对象。

    从第 5 行和第 6 行打印的对象内容看,原对象 p1 和克隆出来的对象 p2 内容完全相同。

    代码中我们只是更改了克隆对象 p2 的属性 Address 为湖北省荆州市(原对象 p1 是湖北省武汉市) ,但是从第 7 行和第 8 行打印结果来看,原对象 p1 和克隆对象 p2 的 Address 属性都被修改了。

    也就是说对象 Person 的属性 Address,经过 clone 之后,其实只是复制了其引用,他们指向的还是同一块堆内存空间,当修改其中一个对象的属性 Address,另一个也会跟着变化。

    6aa67bcdf042b0c83fdfc54ce44d9f3c.png

    浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。

    6、深拷贝

    弄清楚了浅拷贝,那么深拷贝就很容易理解了。

    深拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。

    c71664f8fdaccc7c9af715835e0e3c25.png

    那么该如何实现深拷贝呢?Object 类提供的 clone 是只能实现 浅拷贝的。

    7、如何实现深拷贝?

    深拷贝的原理我们知道了,就是要让原始对象和克隆之后的对象所具有的引用类型属性不是指向同一块堆内存,这里有三种实现思路。

    ①、让每个引用类型属性内部都重写clone() 方法

    既然引用类型不能实现深拷贝,那么我们将每个引用类型都拆分为基本类型,分别进行浅拷贝。比如上面的例子,Person 类有一个引用类型 Address(其实String 也是引用类型,但是String类型有点特殊,后面会详细讲解),我们在 Address 类内部也重写 clone 方法。如下:

    Address.class:

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 packagecom.ys.test;2

    3 public class Address implementsCloneable{4 privateString provices;5 privateString city;6 public voidsetAddress(String provices,String city){7 this.provices =provices;8 this.city =city;9 }10 @Override11 publicString toString() {12 return "Address [provices=" + provices + ", city=" + city + "]";13 }14 @Override15 protected Object clone() throwsCloneNotSupportedException {16 return super.clone();17 }18

    19 }

    View Code

    Person.class 的 clone() 方法:

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 @Override2 protected Object clone() throwsCloneNotSupportedException {3 Person p = (Person) super.clone();4 p.address =(Address) address.clone();5 returnp;6 }

    View Code

    测试还是和上面一样,我们会发现更改了p2对象的Address属性,p1 对象的 Address 属性并没有变化。

    但是这种做法有个弊端,这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。

    ②、利用序列化

    序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。

    注意每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    //深度拷贝

    public Object deepClone() throwsException{//序列化

    ByteArrayOutputStream bos = newByteArrayOutputStream();

    ObjectOutputStream oos= newObjectOutputStream(bos);

    oos.writeObject(this);//反序列化

    ByteArrayInputStream bis = newByteArrayInputStream(bos.toByteArray());

    ObjectInputStream ois= newObjectInputStream(bis);returnois.readObject();

    }

    View Code

    因为序列化产生的是两个完全独立的对象,所有无论嵌套多少个引用类型,序列化都是能实现深拷贝的。

    展开全文
  • 面试题目:如何实现对一个数组或对象的浅拷贝和深拷贝?WTF,复制还分两种,第一次遇到这种问题的时候很是无语呢,先来看看一般的答案的理解。浅拷贝是只拷贝一层,深层次的对象级别就只拷贝引用。 深拷贝是拷贝多层...

    面试题目:如何实现对一个数组或对象的浅拷贝和深拷贝?

    WTF,复制还分两种,第一次遇到这种问题的时候很是无语呢,先来看看一般的答案的理解。

    浅拷贝是只拷贝一层,深层次的对象级别就只拷贝引用。 深拷贝是拷贝多层,每一级别的数据都拷贝出来。也就是说,基本数据类型其实不存在深浅拷贝的问题,只有对象和数组才存在深浅拷贝的问题。

    主要解决的是什么问题呢?你去买房子,看中一套不错要了,然后中介给你打印了一份合同,你签字付钱。过一段时间去看,哎呀我去,怎么装修了?另外一个人也拿着同样有合同、付款凭证。我以为是我买的房子,结果中介一房两卖,别人也能搞。这怎么行?

    JS数据类型

    js分为基本数据类型和复杂数据类型。

    基本数据类型包括:String、Number、Boolean、Null、Undefined

    复杂(引用)类型包括:Object、Array、Function

    在开发过程中,经常使用 typeof 来检测数据类型。默认var声明的时候,如果不进行赋值,类型就是undefined。布尔值是boolean只有 true,false两种值。

    声明的时候用的null,这时候代表空对象,使用typeof检测的时候,显示是Object。

    JS内存管理

    JS代码运行的时候,数据都要写入内存进行调用的,而不同的数据类型在内存中存放的方式是不一样的。

    基本数据类型是存储在栈数据空间中,复杂数据类型是存储在堆数据空间中的,而对数据空间不能直接访问,需要栈这边进行位置指引。

    一个不是很恰当的比喻就是内存相当于仓库。

    仓库里面分了两个区域,一边是都是小格子,另一边都是大货柜。简单数据类型比如你的一本书,你的一份账单什么的就直接放在小格子里面就好了。

    另外你有一屋子书和一屋子的账单,小格子放不下。你就租了一个小格子和一个仓库。小格子里面放着仓库的钥匙和仓库的位置,仓库里面放东西。

    实际的内存读取方式也类似。要找自己的小格子,你就要从上到下挨着找。想要找自己货柜里面的东西,还是需要先去小格子里面找到存放钥匙和位置的格子,找到以后直接去找货柜。

    下图是

    浅拷贝和深拷贝

    再没有了解到深拷贝和浅拷贝知识的时候,一般拷贝就是从新赋值。声明个数据直接用另外一个对象赋值。这个就算是浅拷贝。

    当遇到复杂对象的时候,复制的只是对象的指针,并没有重新开辟大的空间进行复制。这时候造成的影响就是对两个指针进行数据操作的时候,操作的是同一个数据内容,相互之间是受影响的。

    而深拷贝就是需要连指针到内容都进行复制,两个指针指向两个空间的内容。各自操作已经不受影响。

    浅拷贝的实现方式

    浅拷贝的复制就是直接复制赋值就可以了。

    方法一:

    function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj;}var obj = { b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); }}var cloneObj = simpleClone(obj); console.log(cloneObj.b); console.log(cloneObj.c);console.log(cloneObj.d);cloneObj.b.a = "changed";cloneObj.c = [1, 2, 3];cloneObj.d = function() { alert("changed"); };console.log(obj.b);console.log(obj.c);console.log(obj.d);自行运行查看下变化及原因。

    方法二: Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

    Object.assign(target, ...sources)var obj = { a: {a: "hello", b: 21} };var initalObj = Object.assign({}, obj);initalObj.a.a = "changed";console.log(obj.a.a); // "changed"需要注意的是:

    Object.assign()可以处理一层的深度拷贝,如下:

    var obj1 = { a: 10, b: 20, c: 30 };var obj2 = Object.assign({}, obj1);obj2.b = 100;console.log(obj1);// { a: 10, b: 20, c: 30 }

    如果要复制的对象只有一层,对象里面的元素全是基本元素的话,前面的浅拷贝案例其实就完成了深拷贝的功能。我们重点说一下多层对象。

    1.通过JSON转换 用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

    但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。并且只能处理常见的Number, String, Boolean, Array, 扁平对象等这些能被JSON表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

    2.递归拷贝

    function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {}; arguments.callee(prop, obj[i]); } else { obj[i] = prop; } } return obj;}递归拷贝就是将对象逐层解开进行剖析,逐层新建对象,逐层复制,知道最深处的所有简单数据都复制上。

    但是要注意要注意对象引用对象的情况,会掉入死循环。

    3.使用Object.create()方法 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

    function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj;}小案例

    之前在进行公司VUE项目开发的过程中,由于需要将富文本编辑器抽离成为一个单独的组件,然后将内容的对象传入进去。如果按照传统的vue组件开发的流程,肯定是要接收传入、赋值给本组件、本组件编辑器修改、修改完毕的内容再进行emit外传,然后组件外部接受,进一步处理。

    但是由于vue组件之间浅拷贝的特性,其实传入的对象修改之后,外部组件直接取值拿到的就是最新的值。

    也是因为这个发现才对深拷贝和浅拷贝有了更加深入的了解。

    展开全文
  • 一、前言任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以...

    一、前言

    任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以排查的问题。

    本文就在 Java 中的深拷贝和浅拷贝做一个详细的解说。

    二、什么是浅拷贝和深拷贝

    首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。

    在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

    而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

    所以到现在,就应该了解了,所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已。

    总结来说:

    1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。

    cd475719994e5144d036ee6d683370d5.png

    2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

    25cebb48cb1b9a075258051aff0c479d.png

    三、Java 中的 clone()

    3.1 Object 上的 clone() 方法

    在 Java 中,所有的 Class 都继承自 Object ,而在 Object 上,存在一个 clone() 方法,它被声明为了 protected ,所以我们可以在其子类中,使用它。

    而无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。

    9d601f33cfd92d8abcfcdba60dc7b61b.png

    可以看到,它的实现非常的简单,它限制所有调用 clone() 方法的对象,都必须实现 Cloneable 接口,否者将抛出 CloneNotSupportedException 这个异常。最终会调用 internalClone() 方法来完成具体的操作。而 internalClone() 方法,实则是一个 native 的方法。对此我们就没必要深究了,只需要知道它可以 clone() 一个对象得到一个新的对象实例即可。

    ecf5743fd41ca62b46385152f22a3829.png

    而反观 Cloneable 接口,可以看到它其实什么方法都不需要实现。对他可以简单的理解只是一个标记,是开发者允许这个对象被拷贝。

    3.2 浅拷贝

    先来看看浅拷贝的例子。

    首先创建一个 class 为 FatherClass ,对其实现 Cloneable 接口,并且重写 clone() 方法。

    705df1cb0a5137d6abd418f030885369.png

    然后先正常 new 一个 FatherClass 对象,再使用 clone() 方法创建一个新的对象。

    ca602b1a3e98c78a396a54becbb24c8c.png

    最后看看输出的 Log :

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 560973324

    I/cxmyDev: fatherB hash : 560938740

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    可以看到,使用 clone() 方法,从 == 和 hashCode 的不同可以看出,clone() 方法实则是真的创建了一个新的对象。

    但这只是一次浅拷贝的操作。

    来验证这一点,继续看下去,在 FatherClass 中,还有一个 ChildClass 的对象 child ,clone() 方法是否也可以正常复制它呢?改写一个上面的 Demo。

    4acea3a7fce5e643f09a8c2ef18108c1.png

    看到,这里将其内的 child 进行负责,用起来看看输出的 Log 效果。

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 560975188

    I/cxmyDev: fatherB hash : 560872384

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    I/cxmyDev: ==================

    I/cxmyDev: A.child == B.child : true

    I/cxmyDev: fatherA.child hash : 560891436

    I/cxmyDev: fatherB.child hash : 560891436

    从最后对 child 的输出可以看到,A 和 B 的 child 对象,实际上还是指向了统一个对象,只对对它的引用进行了传递。

    3.3 深拷贝

    既然已经了解了对 clone() 方法,只能对当前对象进行浅拷贝,引用类型依然是在传递引用。

    那么,如何进行一个深拷贝呢?

    比较常用的方案有两种:

    序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。

    继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。

    继续改写上面的 Demo ,让 ChildClass 也实现 Cloneable 接口。

    52ba8b2fee71a2b9c34287538d48c515.png

    最重要的代码就在 FatherClass.clone() 中,它对其内的 child ,再进行了一次 clone() 操作。

    再来看看输出的 Log。

    I/cxmyDev: fatherA == fatherB : false

    I/cxmyDev: fatherA hash : 561056732

    I/cxmyDev: fatherB hash : 561057344

    I/cxmyDev: fatherA name : 张三

    I/cxmyDev: fatherB name : 张三

    I/cxmyDev: ==================

    I/cxmyDev: A.child == B.child : false

    I/cxmyDev: fatherA.child hash : 561057304

    I/cxmyDev: fatherB.child hash : 561057360

    可以看到,对 child 也进行了一次拷贝,这实则是对 ChildClass 进行的浅拷贝,但是对于 FatherClass 而言,则是一次深拷贝。

    其实深拷贝的思路都差不多,序列化也好,使用 clone() 也好,实际上都是需要我们自己来编写拷贝的规则,最终实现深拷贝的目的。

    如果想要实现深拷贝,推荐使用 clone() 方法,这样只需要每个类自己维护自己即可,而无需关心内部其他的对象中,其他的参数是否也需要 clone() 。

    四、总结

    到现在基本上就已经梳理清楚,Java 中浅拷贝和深拷贝的概念了。

    实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。

    展开全文
  • 对于浅拷贝(shallow copy)深度拷贝(deep copy),本节并不打算一上来抛出它们的概念,而是先从它们的操作方法说起,通过代码来理解两者的不同。list1 = [1, 2, 3]list2 = list(list1)print(list2)print("list1==...
  • 文章目录1、深拷贝浅拷贝概览2、赋值、深拷贝浅拷贝区别3、浅拷贝实现方式3.1 Object.assign()3.2 Array.prototype.concat()3.3 Array.prototype.slice()4、深拷贝实现方式4.1 JSON.parse(JSON.stringify(arr))...
  • 浅拷贝被复制的对象的所有的变量都与原对象有相同的值,而所有的引用对象仍然指向原来的对象。换言之,浅拷贝 不复制引用对象。1 class Experience {2 private String skill;3 public void setSkill(String skill){4...
  • 浅谈深拷贝和浅拷贝

    千次阅读 2020-12-30 10:05:20
    深拷贝和浅拷贝说起深拷贝和浅拷贝,首先我们来看两个栗子// 栗子1var a = 1,b=a;console.log(a);console.log(b)b = 2;console.log(a);console.log(b)// 栗子2var obj1 = {x: 1, y: 2}, obj2 = obj1;console.log(obj...
  • 1 4种数组深拷贝package Collection;import com.sun.xml.internal.bind.v2.TODO;import java.util.Arrays;public class Test4 {public static void main(String[] args) {int[] arr = {4, 5, 6, 7};int[] res = copy...
  • 深拷贝和浅拷贝的区别及实现方法

    千次阅读 2021-03-09 19:51:33
    简述深拷贝和浅拷贝的区别以及实现方法
  • Java深拷贝和浅拷贝

    2021-01-27 01:49:19
    目录介绍01.对象拷贝有哪些02.理解浅拷贝2.1 什么是浅拷贝2.2 实现浅拷贝案例03.理解深拷贝3.1 什么是深拷贝3.2 实现深拷贝案例04....集合的拷贝8.1 集合浅拷贝8.2 集合深拷贝好消息博客笔记大汇总【16...
  • 深拷贝 JSON.parse(JSON.stringify(obj))深拷贝已有对象 JSON.stingify(obj)将js中的对象转换成JSON字符串 let jack = { name: 'jack' } console.log(jack) console.log(JSON.stringify(jack)) 它们在格式上有...
  • 前端深拷贝和浅拷贝

    2020-12-20 10:48:58
    在前端攻城狮的工作实际应用中,有很多情况下在处理数据的时候,会用到数据的深拷贝和浅拷贝例如:vue中数据是双向绑定的,页面显示依赖于从后台获取到的数据,但要将这个数据当做参数发送给另外一个接口的时候,...
  • 概述深浅拷贝用法来自copy模块导入模块: import copy浅拷贝: copy.copy深拷贝: copy.deepcopy字面理解直接赋值: 其实就是对象的引用(别名),赋值的两边指向的是同一个对象浅拷贝(copy): 拷贝父对象,不会拷贝对象的...
  • 1.首先,你要知道怎么实现克隆:实现Cloneable接口,在bean里面重写clone()方法,权限为public。2.其次,你要大概知道什么是地址传递,什么是值传递。3.最后,你要知道你为什么使用这个clone方法。先看第一条,简单...
  • java 浅拷贝和深拷贝

    2021-01-17 14:08:45
    实现拷贝有几点:1)实现Cloneable接口2)重写Object类中的clone方法,并将可见性从protect改为public3)克隆需要调用super.clone(),也就是Object的实现方法浅拷贝和深拷贝的区别:浅拷贝是指拷贝对象时仅仅拷贝对象...
  • 二、关于遵循NSCopyNSMutableCopy协议后的copy及mutablCopy区别1、非集合不可变对象,copy是浅拷贝,mutableCopy是深拷贝;2、非集合可变对象,copymutableCopy是深拷贝;3、集合不可变对象,copy是浅拷贝,...
  • 深拷贝和浅拷贝 不拷贝: 如果只是简单的赋值,那么不会进行拷贝。 import numpy as np a= np.arange(10) print(a) b = a #赋值,对相同数值进行不同命名, print(b) print(b is a) [0 1 2 3 4 5 6 7 8...
  • JS深拷贝和浅拷贝

    千次阅读 2021-01-21 23:01:40
    浅拷贝可以用for in 来实现,也可用es6新增方法Object.assign(target,...sources) 来实现,target为目标对象,sources为原对象(要进行拷贝的对象) 浅拷贝后改变target中的值,sources也会进行改变。深拷贝不会这样...
  • Python 深拷贝和浅拷贝

    2020-12-23 18:09:02
    一、熟悉Python内存管理在Python中,变量在第一次赋值时自动声明,在创建---也就是赋值的时候,解释器会根据语法右侧的操作数来决定新对象的类型。引用计数器:一个内部跟踪变量引用计数:每一个对象各有多少个...
  • 拷贝和浅拷贝最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用,深拷贝在计算机中开辟了一块内存地址用于存放复制的对象,而浅拷贝仅仅是指向被拷贝的内存地址,如果原地址中对象被改变了,那么...
  • [js面试题] 深拷贝和浅拷贝,如何实现深拷贝 什么叫深拷贝和浅拷贝,简单一点来说就是引用数据类型的赋值,如果A赋值了B,B发生变化时,A如果也发生变化,叫浅拷贝,如果B/A没有发生变化,叫浅拷贝深拷贝原理 js的...
  • 深拷贝和浅拷贝

    2021-01-23 23:17:17
    使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。 浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。 深复制:在计算机中开辟一块新...
  • 一、java的拷贝分类 (1)、引用拷贝 创建一个指向对象的引用变量的拷贝。 Teacher teacher = new Teacher("Taylor",26); Teacher otherteacher = teacher; System.out.println(teacher); System....
  • 深拷贝和浅拷贝的区别

    千次阅读 2021-02-06 21:37:16
    深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。 假设B复制了A,修改A的时候,看B是否发生变化: 如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值) 如果B没有...
  • 首先深拷贝和浅拷贝都是指拷贝一个对象,而不是句柄。当只拷贝对象中的成员变量和声明的句柄时,称为浅拷贝。而深拷贝会复制对象中的所有成员变量以及对象中嵌套的其他类的实例的内容。 举个例子,比如下面这个套娃...
  • Java 浅拷贝和深拷贝

    2021-01-17 14:08:46
    比如说对象 A 对象 B,二者都是 ClassC 的对象,具有成员变量 a b,现在对对象 A 进行拷贝赋值给 B,也就是 B.a = A.a; B.b = A.b;这时再去改变 B 的属性 a 或者 b 时,可能会遇到问题:假设 a 是基础数据类型...
  • 浅拷贝可变类型浅拷贝copy函数就是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间...[a, b]普通赋值data_mycopy = data -- datadata_mycopy指向同一片空间浅拷贝data_copy = copy.co...
  • 深拷贝和浅拷贝只对引用数据类型而言的!,基本数据类型不存在深浅拷贝 浅拷贝 如果被拷贝对象的成员变量为基本数据类型,那么浅拷贝将会进行值传递,赋值给新对象的成员变量,两者互不关联。 如果被拷贝对象的成员...
  • 总结:深拷贝和浅拷贝区别浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象消耗小。深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。深拷贝把要复制的对象所引用的对象都复制了一遍。一、拷贝的引入(1)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,646
精华内容 39,458
关键字:

深拷贝和浅拷贝的方法