深拷贝,浅拷贝与引用拷贝都是什么
引用拷贝:复制一个对象的引用,而不是复制对象本身。这意味着多个变量可以指向同一个对象,并且对一个变量的操作会影响到其他指向同一对象的变量
浅拷贝:浅拷贝是创建一个新的对象,这个对象有着原始对象的一份精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用拷贝,则拷贝的就是内存地址即引用
深拷贝:深拷贝则是完全复制原始对象及其所有嵌套对象,创建一个独立的,全新的对象,在深拷贝中,原始对象与新对象之间没有任何共享的引用
引用拷贝
引用拷贝的Demo
在上述的代码中。我们使用引用拷贝将p1的引用复制给jp2,并且两次分别输出了p1和p2的属性
可以看到,在第一次输出中p1与p2的属性完全相同,之后我们用p2对属性进行了修改,在第二次输出中我们可以发现p2的属性随着p1的修改和发生了改变。
引用拷贝的特点
1.高效性:引用拷贝不需要复制整个对象, 只需要复制对象的引用,因此在时间和空间上都非常高效,特别是对于大型对象或者复杂的数据结构,引用拷贝可以大大减少内存的使用和复制的成本
2.同步性:由于多个变量指向同一个对象,对一个对象的修改会立即反映在其他变量上,这在某些情况下可以方便地实现数据的同步更新,但也需要注意可能带来的意外副作用
3. 灵活性:引用拷贝可以让我们在不同的部分代码中共享同一个对象,方便进行数据的传递和操作。同时,也可以通过修改引用指向的对象来实现对多个变量的统一更新。
引用拷贝的使用场景
1. 函数参数传递:在很多编程语言中,函数参数的传递可以使用引用拷贝的方式。这样可以避免复制大型对象,提高函数调用的效率。同时,函数内部对参数的修改也可以直接影响到外部的变量。
2. 数据共享:当多个模块或者对象需要共享同一个数据时,可以使用引用拷贝。这样可以确保数据的一致性,同时减少数据的复制和存储成本。
3. 动态数据结构:在一些需要动态修改的数据结构中,如链表、树等,引用拷贝可以方便地实现节点的添加、删除和修改操作。
引用拷贝的注意事项
1. 副作用:由于多个变量指向同一个对象,对一个变量的修改可能会意外地影响到其他变量。在使用引用拷贝时,需要特别注意这种副作用,确保对数据的修改是预期的。
2. 内存管理:当多个变量指向同一个对象时,需要注意对象的生命周期和内存管理。如果其中一个变量不再需要指向该对象,应该及时将其设置为 None 或者其他合适的值,以避免内存泄漏。
3. 线程安全:在多线程环境中,使用引用拷贝需要注意线程安全问题。如果多个线程同时访问和修改同一个对象,可能会导致数据的不一致性。可以使用同步机制来确保线程安全。
浅拷贝
浅拷贝的Demo
在上述代码中,Shallow_Person类实现了Cloneable接口,并且重写了Object的clone方法,将clone方法的权限修饰符改为了public,返回类型改为了Shallow_Person。
p2复制了p1的对象(除了p1对象中引用类型的属性),并且复制了p1对象中引用类型属性的地址
在输出中我们可以看到第一次输出时p1与p2的对象属性完全相同,之后我们对p2的属性进行了修改,可以发现,p2的基本类型属性和String类型属性与p1的基本类型属性和String类型属性不同,而p2的引用类型属性与p1的引用类型属性相同。这是因为浅拷贝完全复制了基本数据类型的属性,但是只复制了引用类型属性的地址(String在这里是特殊的,它也是完全复制了属性而非地址)。
浅拷贝的特点
1.对于不可变类型数据
如果原始对象包含不可变数据类型,如数字、字符串等,浅拷贝后修改新对象中这些不可变元素的值,不会影响原始对象。
2.对于可变数据类型
当原始对象包含可变数据类型,如列表、字典等,浅拷贝后新对象中的可变元素与原始对象中的相应元素指向同一个内存地址。
所以,如果修改新对象中的可变元素,原始对象中的相应元素也会被修改。比如修改 b[2][0] = 7 ,那么 a[2][0] 也会变成 7。
浅拷贝的应用场景
快速创建结构类似的对象
当我们需要一个与现有对象结构相似但不完全相同的新对象时,浅拷贝可以快速实现。例如,在处理一些数据时,我们可能需要对原始数据进行一些修改,但又不想完全改变原始数据,这时浅拷贝就很有用。
节省内存
由于浅拷贝只是对原始对象的部分引用,而不是完全复制所有内容,所以在某些情况下可以节省内存空间。
注意事项
理解引用关系
在使用浅拷贝时,一定要清楚新对象和原始对象中可变元素的引用关系,避免因为修改新对象而意外改变原始对象。
局限性
浅拷贝并不适用于所有情况。如果需要完全独立的对象,即修改新对象不会影响原始对象,那么可能需要使用深拷贝。
深拷贝
深拷贝的Deom
深拷贝的代码只对浅拷贝中重写Object的clone方法部分进行了修改,在其中将person和其的引用类型属性手动进行了拷贝,之后返回person。
通过输出结果我们可以发现在深拷贝中无论是基本类型数据,String类型数据还是引用类型数据都与原本的不同,这是因为深拷贝完全复制了原本的对象。
深拷贝的优势与使用场景
数据独立性
深拷贝确保了复制后的对象与原始对象完全独立,避免了意外修改原始对象的风险。在多线程环境中或者需要对数据进行不同处理而不影响原始数据的情况下,深拷贝非常有用。
复杂对象结构处理
当对象的结构比较复杂,包含多个层次的对象引用时,深拷贝可以确保整个对象层次结构都被完整地复制,而不是仅仅复制表面的引用。
注意事项
性能开销
深拷贝通常比浅拷贝需要更多的时间和资源,特别是对于大型复杂对象。在性能敏感的场景中,需要谨慎考虑是否使用深拷贝。
循环引用
如果对象之间存在循环引用,序列化和反序列化的方法可能会导致无限循环。在这种情况下,需要特别处理循环引用的情况,或者采用其他深拷贝方法。