也谈java浅克隆与深克隆

如果我们有一个对象a,我们想得到它的一个克隆,那么我们该怎么做呢?最直观、最笨的方法是我们先new一个a的同类对象b,然后挨个拷贝a的属性给b中的相应属性。那么,这里马上就得引出两个概念:浅克隆与深克隆。

如果用直白的、非严格定义的语言来解释这两个概念,那么可以这么说:

所谓浅克隆是指复制一个对象的实例,但是这个对象中包含的其它的对象还是共用的。

所谓深克隆是指复制一个对象的实例,而且这个对象中包含的其它的对象也要复制一份。

如果我们要深克隆一个对象,而这个对象又比较复杂,它还包含n多其它对象的引用,那么要用我们开始说的那种克隆方法,简直是要人命! 即便是我们只要一个浅克隆的对象,如果这个对象有几十上百个基本属性,我们挨个去复制也是不可接受的!

那么,我们先来看看java中浅克隆一个对象可以怎么做。

Object 类提供有一个clone方法,它的方法签名如下:

 protected native Object clone() throws CloneNotSupportedException;

可以看到:(1)它是一个native方法。native方法的效率一般来说都远高于非native方法。

                    (2)它是一个protected方法。

                    (3)它返回一个Object。

如果我们要在其它类中调用clone方法,那么我们就要重写这个方法,将它的方法属性改为public 。这个方法其实就提供了浅克隆的功能。

验证的代码如下:

Swallow.java:

package com.myclone.test;


public class Swallow implements Cloneable{


	private String name;
	private Wing leftWing;
	private Wing rightWing;
	
	public Swallow(String name, Wing leftWing, Wing rightWing){
		this.name = name;
		this.leftWing = leftWing;
		this.rightWing = rightWing;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Wing getLeftWing() {
		return leftWing;
	}

	public void setLeftWing(Wing leftWing) {
		this.leftWing = leftWing;
	}

	public Wing getRightWing() {
		return rightWing;
	}

	public void setRightWing(Wing rightWing) {
		this.rightWing = rightWing;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO 自动生成的方法存根
		return super.clone();
	}
}

Wing.java:

package com.myclone.test;

public class Wing{

	private int width;

	public Wing(int width){
		this.width = width;
	}
	
	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}
}

Main.java:

package com.myclone.test;

public class Main {

    public static void main(String[] args) throws CloneNotSupportedException {
        // TODO 自动生成的方法存根

        Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));
        Swallow s2 = (Swallow) s1.clone();
        
        System.out.println("(s1==s2)="+ (s1==s2));
        System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());
        System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));
        System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));    
    }
}


运行结果:

(s1==s2)=false
s1.name=大雁a, s2.name=大雁a
(s1.leftWing==s2.leftWing) = true
(s1.rightWing==s2.rightWing) = true

从运行结果中我们可以看出,调用 s1.clone() 方法我们得到的是另一个对象,它浅克隆了s1,因为s1和s2中的对象属性 leftWing 指向的是同一个实例,rightWing指向的是同一个实例。注意:要调用clone方法,必须要实现 Cloneable接口,在我们这个例子中即Swallow实现了Cloneable接口。


如果我们需要深克隆的对象呢?首先我们想到的是,可以再次重写Swallow中的clone方法,将leftWing 和 rightWing 克隆一份。那么Wing 也要重写它的clone方法,改写后的Wing类:

package com.myclone.test;

public class Wing implements Cloneable{

	private int width;

	public Wing(int width){
		this.width = width;
	}
	
	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO 自动生成的方法存根
		return super.clone();
	}
}

改写后的Swallow类:

package com.myclone.test;


public class Swallow implements Cloneable{


	private String name;
	private Wing leftWing;
	private Wing rightWing;
	
	public Swallow(String name, Wing leftWing, Wing rightWing){
		this.name = name;
		this.leftWing = leftWing;
		this.rightWing = rightWing;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Wing getLeftWing() {
		return leftWing;
	}

	public void setLeftWing(Wing leftWing) {
		this.leftWing = leftWing;
	}

	public Wing getRightWing() {
		return rightWing;
	}

	public void setRightWing(Wing rightWing) {
		this.rightWing = rightWing;
	}
	
//	@Override
//	protected Object clone() throws CloneNotSupportedException {
//		// TODO 自动生成的方法存根
//		return super.clone();
//	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO 自动生成的方法存根
		Swallow swallow = (Swallow) super.clone();
		Wing lefWing = (Wing) swallow.getLeftWing().clone();
		Wing rightWing = (Wing) swallow.getRightWing().clone();
		swallow.setLeftWing(lefWing);
		swallow.setRightWing(rightWing);
		return swallow;
	}
}

再次运行Main,得到的结果是:

(s1==s2)=false
s1.name=大雁a, s2.name=大雁a
(s1.leftWing==s2.leftWing) = false
(s1.rightWing==s2.rightWing) = false

可以看出s2已经是s1的深克隆了。这种方法有个缺点就是,如果Wing还含有对象属性的话,那么我们就必须依次去重写属性类的clone方法!

另一种方法是通过序列化和反序列化来实现深克隆。这种方法实际上就是“先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流中,再从流中读出来,便可以重建对象。” ——java.lang.Object.clone()分析

要注意的是所有相关的类都要实现Serializable接口,改写后的实例:

Wing.java

package com.myclone.test;

import java.io.Serializable;

public class Wing implements Cloneable, Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -6757262538695878009L;
	private int width;

	public Wing(int width){
		this.width = width;
	}
	
	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO 自动生成的方法存根
		return super.clone();
	}
}

Swallow.java

package com.myclone.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class Swallow implements Cloneable, Serializable{


	/**
	 * 
	 */
	private static final long serialVersionUID = -2152309743441152834L;
	private String name;
	private Wing leftWing;
	private Wing rightWing;
	
	public Swallow(String name, Wing leftWing, Wing rightWing){
		this.name = name;
		this.leftWing = leftWing;
		this.rightWing = rightWing;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Wing getLeftWing() {
		return leftWing;
	}

	public void setLeftWing(Wing leftWing) {
		this.leftWing = leftWing;
	}

	public Wing getRightWing() {
		return rightWing;
	}

	public void setRightWing(Wing rightWing) {
		this.rightWing = rightWing;
	}
	
//	@Override
//	protected Object clone() throws CloneNotSupportedException {
//		// TODO 自动生成的方法存根
//		return super.clone();
//	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO 自动生成的方法存根
		Swallow swallow = (Swallow) super.clone();
		Wing lefWing = (Wing) swallow.getLeftWing().clone();
		Wing rightWing = (Wing) swallow.getRightWing().clone();
		swallow.setLeftWing(lefWing);
		swallow.setRightWing(rightWing);
		return swallow;
	}
	
	/**
	 * 深克隆
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Object deepClone() throws IOException, ClassNotFoundException{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(this);
		
		ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(bais);
		return ois.readObject();
	}
}

Main.java:

package com.myclone.test;

import java.io.IOException;

public class Main {

	public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
		// TODO 自动生成的方法存根

		Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));
		//Swallow s2 = (Swallow) s1.clone();
		Swallow s2 = (Swallow) s1.deepClone();
		
		System.out.println("(s1==s2)="+ (s1==s2));
		System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());
		System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));
		System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));	 
	}
}

运行结果:

(s1==s2)=false
s1.name=大雁a, s2.name=大雁a
(s1.leftWing==s2.leftWing) = false
(s1.rightWing==s2.rightWing) = false


由此可见,通过序列化和反序列化来实现深克隆是最优雅、最方便的。

2015.09.02  补充:

上午有个同事说深克隆还有一个方法,就是用json,这的确是一个好方法!那么,我们可以在Swallow 类中再加一个deepClone2方法,效果是一样的:

public Swallow deepClone2(){
        Gson gson = new Gson();
        String json = gson.toJson(this);
        return gson.fromJson(json, this.getClass());
    }
似乎更简单一些~ 不过当时我就在想这样效率是否会低一些,测试了下10万次克隆,结果如下:

deepClone 方法耗时=1564
deepClone2 方法耗时=4029



  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值