(11):谨慎的覆盖clone

当我们想要克隆一个对象时,也就是说我们想要两个一模一样的对象,但想要这两个对象各自开辟空间的时候,我们通常要实现cloneable接口。但是接口没给我们提供clone的实现,但Object类却给我们提供了一个受保护的clone实现。

一般来讲我们直接在子类重写的clone方法调用super,clone()可以得到我们想要的结果。

import java.util.Date;
public class User implements Cloneable {
	private String username;
	private String password;
	private Date birthdate;
	public User(String username, String password, Date birthdate) {
		this.username = username;
		this.password = password;
		this.birthdate = birthdate;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
	@Override
	public int hashCode() {
		// 省略equals的实现(可用eclipse自动生成)
	}
	@Override
	public boolean equals(Object obj) {
		// 省略equals的实现(可用eclipse自动生成)
	}
	// 省略一大堆get/set方法
}
import java.util.Date;
import org.junit.Test;
public class TestCase {
	
	@Test
	public void testUserClone() throws CloneNotSupportedException {
		User u1 = new User("Kent", "123456", new Date());
		User u2 = u1;
		User u3 = (User) u1.clone();
		
		System.out.println(u1 == u2);		// true
		System.out.println(u1.equals(u2));	// true
		
		System.out.println(u1 == u3);		// false
		System.out.println(u1.equals(u3));	// true
	}
	
}

如上诉例子所示,我们得到了我们预期的结果,但也有时结果会和预期相去甚远。

public class Administrator implements Cloneable {
	private User user;
	private Boolean editable;
	public Administrator(User user, Boolean editable) {
		this.user = user;
		this.editable = editable;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
	@Override
	public int hashCode() {
		// 老规矩
	}
	@Override
	public boolean equals(Object obj) {
		// 老规矩
	}
	// 老规矩
}
import java.util.Date;
import org.junit.Test;
public class TestCase {
	@Test
	public void testAdministratorClone() throws CloneNotSupportedException {
		Administrator a1 = new Administrator(new User("Kent", "123456", new Date()), true);
		Administrator a2 = a1;
		Administrator a3 = (Administrator) a1.clone();
		
		System.out.println(a1 == a2);			// true
		System.out.println(a1.equals(a2));		// true
		
		System.out.println(a1 == a3);			// false
		System.out.println(a1.equals(a3));		// true
		
		System.out.println(a1.getUser() == a3.getUser());		//true ! It's not our expected!!!!!
		System.out.println(a1.getUser().equals(a3.getUser()));	//true
	}
}
这里我们就可以引入两个专业的术语:浅克隆(shallow clone)和深克隆(deep clone)。
所谓的浅克隆,顾名思义就是很表面的很表层的克隆,如果我们要克隆Administrator对象,只克隆他自身以及他包含的所有对象的引用地址。
而深克隆,就是非浅克隆。克隆除自身以外所有的对象,包括自身所包含的所有对象实例。至于深克隆的层次,由具体的需求决定,也有“N层克隆”一说。
但是,所有的基本(primitive)类型数据,无论是浅克隆还是深克隆,都会进行原值克隆。毕竟他们都不是对象,不是存储在堆中。注意:基本数据类型并不包括他们对应的包装类。
如果我们想让对象进行深度克隆,我们可以这样修改Administrator类。

@Override
protected Object clone() throws CloneNotSupportedException {
	Administrator admin = (Administrator) super.clone();
	admin.user = (User) admin.user.clone();
	return admin;
}
简而言之,所有实现了Cloneable接口的类都应该有一个公有的类覆盖clone。此方法先调用super. clone,然后修正需要修正的域。cloneable有很多问题,我们可以使用拷贝构造器或者拷贝工厂去代替它。
public Yum(Yum yum);
public static Yum newInstance(Yum yum)
这两个做法都比实现Cloneable接口具有更多优势。事实上许多专家级的程序员重来不去覆盖clone()方法也重来不去调用它。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值