python与Java中的深拷贝与浅拷贝

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

深拷贝和浅拷贝都是对象的拷贝,都会生成一个看起来相同的对象,他们本质的区别是拷贝出来的对象的地址是否和原对象一样,也就是地址的复制还是值的复制的区别。


提示:以下是本篇文章正文内容,下面案例可供参考

一、python

1.1 什么是可变对象,什么是不可变对象:

可变对象是指,一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值;

不可变对象是指,一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了,相当于你把这个对象指向的值复制出来一份,然后做了修改后存到另一个地址上了,但是可变对象就不会做这样的动作,而是直接在对象所指的地址上把值给改变了,而这个对象依然指向这个地址。

1.2 深拷贝和浅拷贝需要注意的地方就是可变元素的拷贝:

在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝它拷贝的是浅层次的数据结构(不可变元素),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去,而是和原对象里的可变元素指向同一个地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象是同时改变的,但是深拷贝不会这样,这个是浅拷贝相对于深拷贝最根本的区别。

举例说明:

#encoding=utf-8

import copy
a=[1,2,3,4,5,[‘a’,‘b’]]
#原始对象
b=a#赋值,传对象的引用
c=copy.copy(a)#对象拷贝,浅拷贝
d=copy.deepcopy(a)#对象拷贝,深拷贝
print “a=,a," id(a)=",id(a),id(a[5])=,id(a[5])
print “b=,b," id(b)=",id(b),id(b[5])=,id(b[5])
print “c=,c," id©=",id©,id(c[5])=,id(c[5])
print “d=,d," id(d)=",id(d),id(d[5])=,id(d[5])
print “**70

a.append(6)#修改对象a
a[5].append(‘c’)#修改对象a中的[‘a’,‘b’]数组对象
print “a=,a," id(a)=",id(a),id(a[5])=,id(a[5])
print “b=,b," id(b)=",id(b),id(b[5])=,id(b[5])
print “c=,c," id©=",id©,id(c[5])=,id(c[5])
print “d=,d," id(d)=",id(d),id(d[5])=,id(d[5])

结果:

从程序的结果来看,列表a和b是赋值操作,两个对象完全指向同一个地址,a和b就是同一块地址的两个引用,其实就是一个东西,所以一个对象在修改浅层元素(不可变)或深层元素(可变)时,另一个对象也同时在变;

c是a进行浅拷贝生成的对象,可以看到a(或b)和c两个对象整体的id是不同的,但是里面的第5个元素-列表的地址却是相同的(指向同一个地址),所以b在浅层次元素层面(不可变)增加一个元素时,c并没跟着增加,但是b的第5个元素-列表在增加一个元素时,c的第5个元素也跟着增加了,这就是因为b和c的第5个元素-列表是指向同一个地址的,这个地址上的值变了,在两个地方会同时改变;

再看d,d的浅层次元素(不可变)和 深层次元素(可变)的地址和a,b,c都不一样,所以,a,b,c无论怎么修改,d都不会跟着改变,这就是深拷贝的结果。

也可以这样理解:

深拷贝就是完全跟以前就没有任何关系了,原来的对象怎么改都不会影响当前对象

浅拷贝,原对象的list元素改变的话会改变当前对象,如果当前对象中list元素改变了,也同样会影响原对象。

通常复制的时候要用深拷贝,因为浅拷贝后,两个对象中不可变对象指向不同地址,相互不会改变,但是两个对象中的可变元素是指向相同的地址,一个变了,另一个会同时改变,会有影响(list是可变对象)。
1.3 如果要让原list和copy list没有影响怎么办?

用深拷贝,拷贝后完全开辟新的内存地址来保存之前的对象,虽然可能地址执行的内容可能相同(同一个地址,例如’s’),但是不会相互影响。

比如:

List1=[‘a’,’b’,’c’]

List2=[‘a’,’b’,’c’]

两个列表中的’a’的地址是相同的

Id(list1[0])=id(list2[0]),但是两个列表的地址是不同的

通常来讲不可变元素包含:

int,float,complex,long,str,unicode,tuple

二、java

Object类中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。在Java中,如果需要克隆的类中只有基本类型的话,那么只在该类中实现Cloneable接口即可,如果该类中除了包含基本类型之外,还有引用类型的话,需要将该引用对象也要实现Cloneable接口,并重写clone方法。

2.1 浅拷贝
public class Address{
    private String city;

    public Address(String city) {
        this.city = city;
    }
	
    // ... getter and setter

    // ... toString
}

public class Employee implements Cloneable{
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // ... getter and setter

    // ... toString

    @Override
    protected Employee clone(){
        Employee employee = null;
        try {
            employee = (Employee)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return employee;
    }
}

@Test
public void testClone(){
    System.out.println("克隆之前");
    Address address = new Address("深圳市");
    Employee employee_before = new Employee("been", 18, address);
    System.out.println("employee_before: "+ employee_before);

    System.out.println("克隆之后");
    Employee employee_after = employee.clone();

    employee1.getAddress().setCity("广州市");
    employee1.setName("mad");
    employee1.setAge(23);
    
    System.out.println("employee_after:  " + employee_after);

    System.out.println("employee_before: " + employee_before);
}

输出结果会发现,第一个员工对象的名字和年龄和第二个员工对象的名字和年龄不一致,说明name(基本类型)和age(基本类型)克隆成功,即修改第二个员工的名字和年龄不会影响到第一个员工的名字和年龄。

但是,修改完第二个员工的地址后发现第一个员工的地址也发生了变化,说明 Address(引用类型)克隆不成功,即修改的是同一个对象,如果想把Address(引用类型)克隆成功,需要深拷贝。

2.2 深拷贝

要想实现 Address(引用类型)的深克隆,首先让Address类实现 Cloneable 接口,重写clone方法。

在Employee类的clone方法里添加一行代码:employee.address = address.clone();

总结

两者的区别:
就是关于需要clone的对象中如果包含引用对象时,是否将引用对象也进行一个clone(重新在堆空间中创建一个对象)
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。(仅仅拷贝对象本身,而不拷贝对象包含的引用指向的对象)
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建⼀个新的对象,并复制其内容,此为深拷贝。(不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值