J2SE(十八)Object之Clone

前言

        在我们编码过程中,我们有时候会遇见这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同的新对象B,并且此后对B的任何改动都不能影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中实现它的方法有很多,今天我们就来探讨一种简单而高效的实现方法,即通过Object的clone方法实现。

一、相关类

Java中跟克隆有关的两个类分别是Cloneable接口和Object类中的clone方法,通过两者的协作来实现克隆。Clonable接口api描述如下:

java.lang.Cloneable 接口(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)
此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。 如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException异常。 
按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。 
注意,此接口不包含 clone 方法。因此,因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
       上面的意思是Cloneable接口没有任何方法,仅是个标志接口(tagging interface),若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法。
二、例子

public class Person implements Cloneable{
	
	private int id;
	
	private String name;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
测试方法

public static void main(String[] args) throws CloneNotSupportedException {
		Person p=new Person(1, "zhangsan");
		Person p1=(Person) p.clone();
		p1.setName("wangwu");
		System.out.println(p);
		System.out.println(p1);
		System.out.println(p==p1);
		System.out.println(p.equals(p1));
	}
结果集

Person [id=1, name=zhangsan]
Person [id=1, name=wangwu]
false
false
从结果集可以看出,通过Person p的clone方法得到了Person p1,p与p1不相等且对p1做的修改不影响p。
三、浅克隆与深度克隆

        从上面的程序我们可以看出,clone调用了object的clone方法,创建一个对象然后一一赋值。对于原始类型跟不可变类型(String,Long...)来说是这种方法是没有问题的,但是如果是复杂类型比如list的话,那么clone对象跟原始对象会指向同一个对象。
我们将上面的代码做一定的修改,在Person对象中添加属性List<String> list

private List<String> list;
	
	public List<String> getList() {
		return list;
	}

	public void setList(List<String> list) {
		this.list = list;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", list=" + list + "]";
	}

	public Person(int id, String name, List<String> list) {
		super();
		this.id = id;
		this.name = name;
		this.list = list;
	}
测试代码修改为

public static void main(String[] args) throws CloneNotSupportedException {
		List<String> list=new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
		Person p=new Person(1, "zhangsan",list);
		Person p1=(Person) p.clone();
		p1.getList().add("d");
		System.out.println(p);
		System.out.println(p1);
		System.out.println(p==p1);
		System.out.println(p.equals(p1));
	}
结果集

Person [id=1, name=zhangsan, list=[a, b, c, d]]
Person [id=1, name=zhangsan, list=[a, b, c, d]]
false
false
        可以看出,p跟p1的List指向同一个应用,对p1的list做了修改影响到p的list值,这种情况称之为浅克隆,即被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。那什么是深克隆呢?深克隆指的是被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。

那么如何实现深克隆呢,常用的方法是采用串行化,重写Person的clone方法,代码如下

	@Override
	public Object clone(){
		 ObjectInputStream oi=null;
		 Object o=null;
		try {
			//将对象写到流里
			 ByteArrayOutputStream bo=new ByteArrayOutputStream();
			 ObjectOutputStream oo=new ObjectOutputStream(bo);
			 oo.writeObject(this);
			 //从流里读出来
			 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
			 oi=new ObjectInputStream(bi);
			 o=oi.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return o;
	}
这样就避免了引用类型的clone问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值