JAVA中的深度克隆(deep clone) 和 影子克隆(shallow clone)浅析

本文深入探讨了Java中的克隆概念,包括影子克隆和深度克隆。影子克隆仅复制基本数据类型,而对象引用仍然指向同一对象。深度克隆则对所有可序列化的对象域进行克隆,确保对象的独立性。文章通过实例展示了如何实现深度克隆,并提到了StringBuffer等特殊类型的克隆问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是"克隆"?

      在开发过程中,我们有一个对象A,其中存储了一些信息,这时我们需要创建一个新对象B,要求B的初始化信息和

A中的信息保持一致,但对B对象随后的信息更改不会影响B的信息。说简单点,对象B的初始化信息依赖于B,但是

A和B是两个完全独立的对象。

2、影子克隆(shallow clone)

    这里我们以实验的方式讲解影子克隆的效果.

    首先我们创建两个类CloneA、CloneB

public class CloneA {
private int i;

public CloneA(int i) {
super();
this.i = i;
}

public void add() {
i++;
}

public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

@Override
public String toString() {
return "CloneA [i=" + i + "]";
}

   }


/////////////////////////////////////////////////////////////////////////////

    public class CloneB implements Cloneable{
  private int v;

  private CloneA cloneA = new CloneA(88);


  public int getV() {
  return v;
  }


  public void setV(int v) {
 this.v = v;
 }


 public CloneA getCloneA() {
 return cloneA;
 }


public void setCloneA(CloneA cloneA) {
this.cloneA = cloneA;
}

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


@Override
public String toString() {
return "CloneB [v=" + v + ", cloneA=" + cloneA + "]";
}

备注:尽管java中实现克隆的方式很多,譬如直接new一个对象,然后

设置原始对象的属性信息就OK了,但是实现Cloneable接口,实现

Clone方法是最高效的。

然后,进行如下测试:

public class main {
public static void main(String[] args) {
CloneB b = new CloneB();
b.setV(123);
/*************克隆对象,改变Clone后的对象属性***************/
CloneB b1 = (CloneB) b.clone();
b1.setV(456);
b1.getCloneA().add();

System.out.println("b:"+b.toString());
System.out.println("==============After Clone==============");
System.out.println("b1:"+b1.toString());

}


测试结果:

b:CloneB [v=123, cloneA=CloneA [i=89]]
==============After Clone==============
b1:CloneB [v=456, cloneA=CloneA [i=89]]

查看结果表明,整型(基本数据类型)已经被克隆了(z),但是对象CloneA仍然指向同一对象(不同引用而已)。

由此我们可以得出对于基本数据类型,调用Object中的Clone方法产生的效果:在内存中开辟一块内存区域,

然后用原始对象的内容初始化该内存区,但是对象内容并未实现真正的克隆,仅是增加了对同一个对象的

引用。这种就是"影子克隆"。

3、深度克隆

所谓深度克隆,实际上是对一个对象的所有可序列化(非静态、没有使用transient修饰)的所有类型域进行

克隆。

在上边例子的基础上,我们稍作修改,进行对象的深度克隆演示。

1)将类CloneA实现Cloneable接口,实现clone方法。

2)在类CloneB中的clone方法中,增加对象类型的域的clone(红色部分为修改)

public class CloneA   implements Cloneable{
private int i;

public CloneA(int i) {
super();
this.i = i;
}


public void add() {
i++;
}



public int getI() {
return i;
}

public void setI(int i) {
this.i = i;
}

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



@Override
public String toString() {
return "CloneA [i=" + i + "]";
}


}

    

///////////////////////////////////////////////////////////////////////////////////////////

public class CloneB implements Cloneable{
private int v;

private CloneA cloneA = new CloneA(88);


public int getV() {
return v;
}


public void setV(int v) {
this.v = v;
}


public CloneA getCloneA() {
return cloneA;
}


public void setCloneA(CloneA cloneA) {
this.cloneA = cloneA;
}

@Override
protected Object clone() {
CloneB o = null; 
try {
o = (CloneB) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
o.cloneA = (CloneA) cloneA.clone();
return o;
}



@Override
public String toString() {
return "CloneB [v=" + v + ", cloneA=" + cloneA + "]";
}
}


执行测试:

b:CloneB [v=123, cloneA=CloneA [i=88]]
==============After Clone==============
b1:CloneB [v=456, cloneA=CloneA [i=89]]

实现了深度复制。

========================================================

注意:并不是所有的类都能通过clone直接实现深度复制,比如

如果类的字段类型是StringBuffer类型,由于StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个final类

这也是说我们也不能用继承的办法间接实现StringBuffer的clone。

不过我们可以在clone方法了改写:

new  StringBuffer()代替 clone()方法。

另外,除了基本数据类型能自动实现深度clone以外,String对象是一个例外。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值