彻底理解克隆、深拷贝和浅拷贝(俩种方法)

1 篇文章 0 订阅

之前在学Javase时没注意到克隆这个方法,后面再网上看博客和面试题是经常看到克隆、深拷贝和浅拷贝这些词语,刚开始看被吓一跳,以为有多复杂,今天下决心给他搞明白,突然发现原来这么简单,给以后的自己一个警醒,往往认为难的其实也没什么,一定要克服心理关。

首先理解克隆:

我们正常复制一个引用类型的数据时,会直接用赋值符号进行赋值,而这种赋值方法其实俩者共用的还是同一个对象,通过其中一个来改变对象中的值,另一个也相应的发生改变。

而克隆则是,完全创造出一个新的对象,有自己的新的地址,只不过初始信息是和原对象是一样的,而克隆实现的方法有俩钟,先来看第一种,Object类中的Clone方法。

public class testone implements Cloneable{
 
	public int i=10;
	
	public Object clone() {
		testone to=null;
		
		try {
			to=(testone) super.clone();
			//调用Object方法进行克隆
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return to;
	}
	
}
//这是重写Object中的Clone方法


public class testtwo {

	public static void main(String[] args) {
		testone to=new testone();
		testone to1=(testone) to.clone();
		System.out.println(to.i);
		System.out.println(to1.i);
		System.out.println("to的地址"+to);
		System.out.println("to1的地址"+to1);
		System.out.println(to==to1);
		
	}
	
}

结果:
10
10
to的地址com.xp.testone@15db9742
to1的地址com.xp.testone@6d06d69c
false

可以看到俩个对象的地址不一样,但是里面的内容却是一样的。

克隆中要注意的几点是:

首先克隆对象必须实现Cloneable这个接口,但是其实这个借口没有任何的方法,它只是标记了这个对象是可以克隆的而已,类似于序列化的接口,但是千万不要以为有没有这个接口都一样,当没有此接口时,克隆会出错。

java.lang.CloneNotSupportedException: com.xp.testone
	at java.lang.Object.clone(Native Method)
	at com.xp.testone.clone(testone.java:11)
	at com.xp.testtwo.main(testtwo.java:7)
Exception in thread "main" java.lang.NullPointerException
	at com.xp.testtwo.main(testtwo.java:9)
10

第二点:克隆的底层是利用Object的Clone方法进行实现的,而Object的Clone方法为native方法,是由C实现的

protected native Object clone() throws CloneNotSupportedException;

克隆就是这些。

接下来看深复制(深拷贝)、浅复制(浅拷贝),这些东西

先假设有三个类,A B C,B中有A的引用,C中又有B的引用。

浅复制就是

那么浅复制就是当复制c时,c中的值确实是发生了克隆,但是c中的B却没有,b种的A也没有发生克隆,这俩个都是简单的引用复制而已。

看代码:

public class people {

	public String s="hello world";
	
}

public class qianClone implements Cloneable{
	
	String name;
	public people p;
	
	public qianClone(String name,people p) {
		this.name=name;
		this.p=p;
	}
	public Object qianClone() {
		qianClone qc=null;
		try {
			qc=(qianClone) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return qc;
		
	}
	
	
}


public static void main(String[] args) {
		people p=new people();
		qianClone qc=new qianClone("tom",p);
		qianClone qcClone=(qianClone) qc.qianClone();
		qcClone.name="lisa";
		qcClone.p.s="bye bye";
		System.out.println(qc);
		System.out.println(qcClone);
		System.out.println(qc.name);
		System.out.println(qcClone.name);
		System.out.println(qc.p.s);
		System.out.println(qc.p.s);
		
	}
	


com.xp.qianClone@15db9742
com.xp.qianClone@6d06d69c
tom
lisa
bye bye
bye bye

 

图中有俩个对象:people和qianClone,其中qianClone中有people的引用,当改变qianClone的克隆对象中的值时原对象没有变,说明这俩者确实发生了克隆,但是当改变其中一个中的people引用时,另外一个1也跟着发生改变,说明这是发生了引用的简单复制,并没有发生克隆,这就是浅复制。

那么深复制又是怎么样的呢?

深复制就是在克隆一个对象时,其内的引用也发生了相应的克隆,都会产生完全没有关联的对象。

看代码:

public class people implements Cloneable{

	public String s="hello world";
	
	public Object peopleClone() {
		people p=null;
		try {
			p=(people) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return p;
	}
}


public class deepClone implements Cloneable{

	
	String name;
	public people p;
	
	public deepClone(String name,people p) {
		this.name=name;
		this.p=p;
	}
	public Object qianClone() {
		deepClone dc=null;
		try {
			dc=(deepClone) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		dc.p=(people) p.peopleClone();
		//对内部引用p在进行一次拷贝
		return dc;
		
	}
		
		
	
}


public static void main(String[] args) {
		people p=new people();
		deepClone dc=new deepClone("tom",p);
		deepClone dcClone=(deepClone) dc.qianClone();
		dcClone.name="lisa";
		dcClone.p.s="bye bye";
		System.out.println(dc);
		System.out.println(dcClone);
		System.out.println(dc.name);
		System.out.println(dcClone.name);
		System.out.println(dc.p.s);
		System.out.println(dc.p.s);
		
	}


com.xp.deepClone@15db9742
com.xp.deepClone@6d06d69c
tom
lisa
hello world
hello world

其实和浅复制是一样的道理,只不过它在people中也重写了Clone方法,在深复制时将people也进行了一次深复制。

以上是实现Cloneable采用Object的Clone方法来实现的

接下来看第二种实现克隆的方法:

采用序列化直接将对象通过流的形式复制一份

这种方式没有浅复制和深复制之分,直接就是深复制。

看代码:

public class people implements Serializable{

	public String s="hello world";
	
	public Object peopleClone() {
		people p=null;
		try {
			p=(people) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return p;
	}
}



public class deepClone implements Serializable {

	
	String name;
	public people p;
	
	public deepClone(String name,people p) {
		this.name=name;
		this.p=p;
	}
	public Object deepClone() throws IOException, ClassNotFoundException {
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		ObjectOutputStream oo=new ObjectOutputStream(bo);
		oo.writeObject(this);
		//这一步是创建对象输出流,将本对象从内存中输出到字节流中
		ByteArrayInputStream bs = new ByteArrayInputStream(bo.toByteArray());
		ObjectInputStream os = new ObjectInputStream(bs);
		return os.readObject();
		//这一步是将字节流中的对象通过输入字节流输入到Object(方法返回值)类中
		
	}
	}


public static void main(String[] args) throws ClassNotFoundException, IOException {
		people p=new people();
		deepClone dc=new deepClone("tom",p);
		deepClone dc1=(deepClone) dc.deepClone();
		//这一步就完成了克隆,接下来就改变值来验证
		dc1.name="lisa";
		dc1.p.s="bye bye";
		System.out.println(dc);
		System.out.println(dc1);
		System.out.println(dc.name);
		System.out.println(dc1.name);
		System.out.println(dc.p.s);
		System.out.println(dc1.p.s);
	}
	

com.xp.deepClone@55f96302
com.xp.deepClone@3b07d329
tom
lisa
hello world
bye bye

在实现这个方法时,我直接就使用对象里面有其他的对象引用来做验证,发现这种方法直接就是深复制,但是看大神博客说是比较耗时。

感受:真的得做验证,一些知识光看会发现很复杂,但是一验证就发现很简单,思路也会很清晰的。

大神博客     https://www.cnblogs.com/xuanxufeng/p/6558330.html

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值