在代码编写中,经常碰到将一个对象赋值给另一个对象的场景,这便是拷贝。在拷贝的过程中,经常分不清是拷贝了引用还是拷贝了对象。
今天就介绍一下引用拷贝、浅拷贝、深拷贝。
引用拷贝
引用拷贝是指将一个对象的引用直接赋值给另一个变量,这两个引用类型的变量指向同一个对象。这样在修改其中一个变量指向的对象时,另一个变量也会随之改变。
实际上引用拷贝并不是真正意义上的拷贝,而是共享同一份数据。
引用拷贝一般发生在传递参数、返回值等场景。在Java中,如果将一个对象作为参数传递给方法,实际上是将该对象的引用传递给了方法,并不是该对象本身的拷贝。
浅拷贝
浅拷贝会创建一个新对象,新对象与旧对象不等,但是其中的属性相等。
若是浅拷贝对象的属性是基本数据类型,则直接拷贝基本数据类型的值。
若是浅拷贝对象的属性是引用数据类型,则拷贝的是对象的内存地址,即复制引用但不复制引用的对象。当修改了对象中的数据时,另一个对象中的数据也会随之改变。
深拷贝
深拷贝会创建一个新对象,对象里的变量也都将复制一份新的。
新对象与旧对象之间完全独立,双方的修改并不会对对方产生影响。
总结
- 引用拷贝一般通过 = 赋值就可以实现。
- 浅拷贝需要在拷贝的类上实现Cloneable接口并重写其clone()方法。
- 深拷贝不仅需要在拷贝的类上实现Cloneable接口并重写其clone()方法,而且clone()方法需要对各个引用都拷贝一遍。若是深拷贝中的引用数量太多了,可以借助序列化来实现深拷贝。
因为序列化后:将二进制字节流内容写到一个媒介(文本或字节数组),然后是从这个媒介读取数据,原对象写入这个媒介后拷贝给clone对象,原对象的修改不会影响clone对象,因为clone对象是从这个媒介读取。
熟悉对象缓存的知道我们经常将Java对象缓存到Redis中,然后还可能从Redis中读取生成Java对象,这就用到序列化和反序列化。一般可以将Java对象存储为字节流或者json串然后反序列化成Java对象。因为序列化会储存对象的属性但是不会也无法存储对象在内存中地址相关信息。所以在反序列化成Java对象时候会重新创建所有的引用对象。
将三种拷贝方式总结一下:
- 浅拷贝只复制了对象本身,不会复制对象内部的数据。
- 深拷贝递归地复制了对象及其所有子对象的内容。
- 引用拷贝是将一个对象的引用赋值给另一个变量,使得两个变量指向同一个对象。