Java中的深拷贝和浅拷贝

1 对象拷贝的必要性

    在java中新建对象实例的最常用的方法是使用new关键字。使用new创建对象轻量级对象时,速度非常快。但是,对于重量级对象,由于对象在构造函数中可能会进行一些复杂且耗时的操作,因此,构造函数的执行时间可能会比较长。这就导致对象的耗时很长。
    为此,可以使用object的clone()方法。

2 clone()方法的简介

    Object.clone()方法可以绕过对象构造函数,快速复制一个对象实例。由于不需要调用对象构造函数,因此,clone()方法不会受到构造函数性能的影响,可以快速生成一个实例。

   

protected native Object clone() throws CloneNotSupportedException
    解释:

  • 首先,这是一个protected方法。protected访问权限的使用说明可以参考:http://zhangjunhd.blog.51cto.com/113473/19287/
  • 其次,这是一个native方法,因此效率较高
  • 最后,要想某个类的实例能够克隆,那么该类必须实现cloneable接口,不然会抛出CloneNotSupportedException
  • 在默认情况下,clone方法生成的实例只是原对象的一个浅拷贝,如果需要深拷贝,则需要重新实现clone方法。下面,我们看一下浅拷贝和深拷贝的区别

3 浅拷贝和深拷贝

    浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。也就是说,对于对象中的field,不管他是基本类型变量还是引用类型的变量,都直接赋值过去,基本类型变量就是把值拷贝过去,引用类型变量就把引用(即地址)拷贝过去。

    深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

   浅拷贝图例:


    深拷贝图例:


4 代码实例

4.1 浅拷贝

public class LearnClone {
	public static void main(String[] args) throws Exception {
		Apple apple=new Apple();
		apple.id=1;
		apple.color="red";
		List<String> list=new ArrayList<String>();
		list.add("hello");
		apple.list=list;
		
		Apple apple2=apple.clone();
		//可以看出,两个对象的地址不同
		System.out.println(apple==apple2);//输出false
		apple2.id=2;
		apple2.list.add("world");
		//可以看出源对象和拷贝对象的基本类型变量相互独立。然后,引用类型的变量指向了一个地址
		System.out.println(apple);
		System.out.println(apple2);
		//输出:id:1,color:red,list:[hello, world]
		//输出:id:2,color:red,list:[hello, world]
	}

}
class Apple implements Cloneable{
	int id;
	String color; 
	List<String> list;
	@Override
	public Apple clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return (Apple)super.clone();
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "id:"+id+",color:"+color+",list:"+list;
	}
}

4.2深拷贝

只需要改写一下浅拷贝的clone函数

class Apple implements Cloneable{
	int id;
	String color; 
	List<String> list;
	@Override
	public Apple clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Apple apple=(Apple)super.clone();
		List<String> list2=new ArrayList<String>();
		for (String str : list) {
			list2.add(str);
		}
		apple.list=list2;
		return apple;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "id:"+id+",color:"+color+",list:"+list;
	}
}

5 通过序列化实现深拷贝

public class LearnDeepCopy {
	public static void main(String[] args) throws Exception{
		List<String> list=new ArrayList<String>();
		list.add("hello");
		Orange orange=new Orange(1, "yellow",list);
		Orange copy=null;
		//将对象写到流里面
		ByteArrayOutputStream byteArrayOutput=new ByteArrayOutputStream();
		ObjectOutputStream objectOutput=new ObjectOutputStream(byteArrayOutput);
		objectOutput.writeObject(orange);
		objectOutput.flush();
		//从流里面在读出对象
		ByteArrayInputStream byteArrayInput=new ByteArrayInputStream(byteArrayOutput.toByteArray());
		ObjectInputStream objectInput=new ObjectInputStream(byteArrayInput);
		copy=(Orange)objectInput.readObject();
		copy.list.add("world");
		//可以发现两个对象的地址不同
		System.out.println("orange==copy:"+(copy==orange));
		//输出:orange==copy:false
		System.out.println(orange);
		//输出:id:1,color:yellow,list:[hello]
		System.out.println(copy);
		//输出:id:1,color:yellow,list:[hello, world]
	}

}

class Orange implements Serializable {
	private int id;
	private String color;
	List<String> list;
	public Orange(int id,String color,List<String> list) {
		// TODO Auto-generated constructor stub
		this.id=id;
		this.color=color;
		this.list=list;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "id:"+id+",color:"+color+",list:"+list;
	}
}
缺点:

  • 因为无法序列化transient变量, 使用这种方法将无法拷贝transient变量。
  • 性能相比clone来说,较慢。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值