Java实例 深层复制、浅层复制和懒惰复制
在面向对象编程中,对象复制是创建一个现有对象的副本,产生的对象被称为对象副本或简单的原始对象的副本。有几种方法来复制一个对象,最常见的是通过复制构造器或克隆。
我们可以将克隆定义为 “创建一个对象的副本 “。浅层、深层和懒惰复制与克隆过程有关。
这实际上是创建副本对象的三种方法。
浅层复制
- 每当我们使用克隆方法的默认实现时,我们会得到对象的浅层拷贝,这意味着它创建了新的实例,并将对象的所有字段复制到新的实例中,并将其作为对象类型返回,我们需要明确地将其铸回我们的原始对象。这就是对象的浅层拷贝。
- 对象类的clone()方法支持对象的浅层拷贝。如果对象在浅层拷贝中包含基元和非基元或引用类型的变量,那么克隆的对象也会指向原始对象所指向的同一个对象,因为只有对象的引用被复制,而不是所指的对象本身。
- 这就是为什么在Java中被称为浅层复制或浅层克隆。如果只有原始类型的字段或Immutable对象,那么Java中的浅拷贝和深拷贝就没有区别。
//code illustrating shallow copy
public class Ex {
private int[] data;
// makes a shallow copy of values
public Ex(int[] values) {
data = values;
}
public void showData() {
System.out.println( Arrays.toString(data) );
}
}
上面的代码显示了浅层复制。data仅仅是指与vals相同的数组。
如果值的元素通过其他引用被改变,这可能导致不愉快的副作用。
public class UsesEx{
public static void main(String[] args) {
int[] vals = {3, 7, 9};
Ex e = new Ex(vals);
e.showData(); // prints out [3, 7, 9]
vals[0] = 13;
e.showData(); // prints out [13, 7, 9]
// Very confusing, because we didn't
// intentionally change anything about
// the object e refers to.
}
}
Output 1 : [3, 7, 9]
Output 2 : [13, 7, 9]
深度拷贝
- 每当我们需要自己的拷贝而不使用默认的实现时,我们称之为深度拷贝,每当我们需要对对象进行深度拷贝时,我们需要根据我们的需要来实现。
- 所以对于深度拷贝,我们需要确保所有的成员类也实现了Cloneable接口,并且覆盖了对象类的clone()方法。
深度复制意味着实际上是创建一个新的数组并复制其值。
// Code explaining deep copy
public class Ex {
private int[] data;
// altered to make a deep copy of values
public Ex(int[] values) {
data = new int[values.length];
for (int i = 0; i < data.length; i++) {
data[i] = values[i];
}
}
public void showData() {
System.out.println(Arrays.toString(data));
}
}
public class UsesEx{
public static void main(String[] args) {
int[] vals = {3, 7, 9};
Ex e = new Ex(vals);
e.showData(); // prints out [3, 7, 9]
vals[0] = 13;
e.showData(); // prints out [3, 7, 9]
// changes in array values will not be
// shown in data values.
}
}
Output 1 : [3, 7, 9]
Output 2 : [3, 7, 9]
对数组vals的改变不会导致数组数据的改变。
何时使用什么
对于浅拷贝和深拷贝的选择没有定义硬性规则,但是通常我们应该记住,如果一个对象只有原始字段,那么显然我们应该选择浅拷贝,但是如果对象有对其他对象的引用,那么根据需求,应该进行浅拷贝或深拷贝。如果引用没有更新,那么就没有必要启动深度拷贝。
懒惰 拷贝
懒惰拷贝可以定义为浅层拷贝和深层拷贝的结合。该机制遵循一个简单的方法–在初始状态下,使用浅拷贝方法。一个计数器也被用来跟踪有多少对象共享数据。当程序想要修改原始对象时,它会检查该对象是否被共享。如果对象是共享的,则启动深度拷贝机制。
小结
在浅度拷贝中,只有原始数据类型的字段被拷贝,而对象的引用没有被拷贝。深度复制涉及原始数据类型和对象引用的复制。对于何时进行浅层复制和何时进行深层复制,没有硬性规定。懒惰复制是这两种方法的组合