Java对象克隆中的浅拷贝与深拷贝
在这篇文章中,我们将看到Java对象克隆中浅拷贝和深拷贝之间的区别。要了解差异,在克隆对象时了解浅拷贝和深拷贝的概念非常重要,因此,我们首先尝试使用浅拷贝和深拷贝的示例来了解概念。
Java对象克隆中的浅表复制
使用clone()方法克隆对象时,会发生按位复制,其中原始对象中每个字段的值都将复制到克隆对象的相应字段中。使用默认的clone()方法在Java中进行这种对象克隆的方式将创建一个Shallow副本。
这被称为浅表副本,因为此创建精确副本的过程适用于原始值,但当原始对象持有对另一个对象的引用时,原始对象与克隆对象之间具有共享状态。
在这种情况下,引用将被复制到克隆对象中,并且两个对象共享该对象引用。
例如,如果有一个MyClass类,它具有另一个对象objA作为字段。
class MyClass implements Cloneable{
private ClassA objA;
...
...
}
如果存在MyClass类的对象myObj,并且该对象被克隆,则objA引用在myObj和myObj的克隆副本之间共享。
浅拷贝
Java示例中的浅表复制
在该示例中,有两个类一和二。在第二类中,引用了一个第一类的对象。
class One implements Cloneable{
int i;
String str;
One(int i, String str){
this.i = i;
this.str = str;
}
// overriding clone method
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
class Two implements Cloneable{
int j;
// Object reference
One obj;
Two(int j, One obj){
this.j = j;
this.obj = obj;
}
public Object clone() throws CloneNotSupportedException{
Two objCloned = (Two) super.clone();
return objCloned;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
public One getObj() {
return obj;
}
public void setObj(One obj) {
this.obj = obj;
}
}
public class CloningDemo {
public static void main(String[] args) {
One one = new One(10, "Clone Test");
Two two = new Two(5, one);
try {
Two objCopy = (Two) two.clone();
System.out.println("Original object- " + two.getJ() + " " + two.getObj().str);
System.out.println("Cloned object- " + + objCopy.getJ() + " " + objCopy.getObj().str);
// modifying field in the referenced object
objCopy.getObj().setStr("New Value");
// Modifying primtive value
objCopy.setJ(25);
System.out.println("---After changing value---");
System.out.println("Original object- " + two.getJ() + " " + two.getObj().str);
System.out.println("Cloned object- " + + objCopy.getJ() + " " + objCopy.getObj().str);
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
Original object- 5 Clone Test Cloned object- 5 Clone Test ---After changing value-- Original object- 5 New Value Cloned object- 25 New Value
从输出中可以看到,当副本中引用对象的字段发生更改时,更改也将反映在原始对象中。复制中完成的原始类型字段更改(j的值更改为25)未反映在原始对象中。
Java对象克隆中的深层复制
为了避免原始对象和克隆对象之间的这种状态共享,在这种情况下,两个对象都反映了引用对象的变异,因此需要深度复制。对于在Java中克隆对象时进行深度复制的情况,即使被引用的对象也是单独创建的,因此两个对象都有各自独立的引用。
例如,如果有一个MyClass类,它具有另一个对象objA作为字段。
class MyClass implements Cloneable{
private ClassA objA;
...
...
}
如果有一个MyClass类的对象myObj并创建了一个深拷贝,那么即使是objA的独立副本也存在于myObj和myObj的克隆副本中。
深拷贝
Java深层复制示例
进行深层复制时,有两种情况,下面将通过示例讨论这两种情况:
1如果引用其对象的类实现了Cloneable接口,那么我们也可以显式调用该对象的clone()方法。
在示例中,我们可以看到在类Two中重写了clone方法,并且也显式调用了所引用对象的clone()方法,以创建该对象的不同副本。
class One implements Cloneable{
int i;
String str;
One(int i, String str){
this.i = i;
this.str = str;
}
// overriding clone method
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
class Two implements Cloneable{
int j;
// Object reference
One obj;
Two(int j, One obj){
this.j = j;
this.obj = obj;
}
public Object clone() throws CloneNotSupportedException{
Two objCloned = (Two) super.clone();
// Explicitly calling clone method for
// object of Class One
objCloned.obj = (One) obj.clone();
return objCloned;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
public One getObj() {
return obj;
}
public void setObj(One obj) {
this.obj = obj;
}
}
public class CloningDemo {
public static void main(String[] args) {
One one = new One(10, "Clone Test");
Two two = new Two(5, one);
try {
Two objCopy = (Two) two.clone();
System.out.println("Original object- " + two.getJ() + " " + two.getObj().str);
System.out.println("Cloned object- " + + objCopy.getJ() + " " + objCopy.getObj().str);
// modifying field in the referenced object
objCopy.getObj().setStr("New Value");
// Modifying primtive value
objCopy.setJ(25);
System.out.println("---After changing value---");
System.out.println("Original object- " + two.getJ() + " " + two.getObj().str);
System.out.println("Cloned object- " + + objCopy.getJ() + " " + objCopy.getObj().str);
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
Original object- 5 Clone Test Cloned object- 5 Clone Test ---After changing value-- Original object- 5 Clone Test Cloned object- 25 New Value
如我们所见,当副本中引用对象中的字段发生更改时,完成深层复制后更改不会反映在原始对象中。
2如果引用其对象的类未实现Cloneable接口,则如果在其上调用clone()方法,则会引发CloneNotSupportedException。在这种情况下,我们需要创建一个新对象,并在clone()方法中为字段显式分配值。
class One{
int i;
String str;
One(int i, String str){
this.i = i;
this.str = str;
}
// overriding clone method
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
class Two implements Cloneable{
int j;
// Object reference
One obj;
Two(int j, One obj){
this.j = j;
this.obj = obj;
}
public Object clone() throws CloneNotSupportedException{
Two objCloned = (Two) super.clone();
// Creating new object
One newObj = new One(12, "New Value");
// assigning new oject to the clone
objCloned.setObj(newObj);
return objCloned;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
public One getObj() {
return obj;
}
public void setObj(One obj) {
this.obj = obj;
}
}
public class CloningDemo {
public static void main(String[] args) {
One one = new One(10, "Clone Test");
Two two = new Two(5, one);
try {
Two objCopy = (Two) two.clone();
System.out.println("Original object- " + two.getJ() + " " + two.getObj().getI() + " " + two.getObj().getStr());
System.out.println("Cloned object- " + + objCopy.getJ() + " " + objCopy.getObj().getI() + " " + objCopy.getObj().getStr());
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
Original object- 5 10 Clone Test Cloned object- 5 12 New Value
Java中的浅拷贝与深拷贝
浅拷贝便宜且易于实现。深拷贝非常昂贵,因为每个引用的对象都必须单独创建。它也更加复杂,因为对象树可能很长。
在浅表复制的情况下,尽管使用其自己的字段集创建了对象的不同副本,但是对象引用是共享的。在深度复制的情况下,即使对于引用的对象,也会创建单独的副本。
默认情况下,对象类的clone方法创建浅表副本。为了创建深层副本,我们需要重写clone方法,并在引用的对象上也调用clone方法。

