结合实例详解clone()函数,Cloneable接口以及深拷贝与浅拷贝的问题

package job;
/**
 * ps:java中的clone()方法的功用类似于C++中的拷贝构造函数
 * 拷贝对象还可以用对象流中转一下获得,需要实现标记型接口Serializable
 * Serializable接口中的方法对程序是不可见的,因此实现了该接口的类不需要实现
 * 额外的方法,当把一个序列化的对象写入ObjuctOutputStream时,JVM就会实现
 * Serializable接口中的方法,将一定格式的文本-对象的序列化信息,写入OBjuceOutputStream
 * 输出流的目的地
 * 使用对象流把一个对象写入文件时不仅需要保证该对象是序列化的,而且该对象的
 * 成员对象也必须是序列化的
*/
import java.io.Serializable;
import java.util.Date;

/**
 * Cloneable是标记型接口(另一个常见的就是Serializable),声明了该接口表明可用clone()方法
 * 如果不声明Cloneable接口使用clone()方法就会报CloneNotSupportException
 * 按惯例要重写protected clone()方法为public的,但是clone()是Object的protected方法
 * 已有一个field to field的浅拷贝实现,因此不像其它接口的抽象方法那样必须重写
 * 但是因为Object中的clone()方法的访问权限是protected的,这就意味着,如果一个对象想使用
 * 该方法得到自己的一个复制品,就必须保证自己的类与Object类在同一个包中,这显然是不可能的,
 * 因为Java不允许用户编写的类拥有java.lang这样的包名(尽管可以编译拥有java.lang包名的类,
 * 但运行时JVM拒绝加载这样的类),为了能让一个对象使用clone()方法,创建该对象的类需要重写(覆盖)
 * clone()方法,并且讲访问权限提升为public权限,为了能使用被覆盖的clone()方法,只需在重写的clone()
 * 方法中使用关键字super调用Object类的clone()方法即可。
 * 再者对象中有非基本类型的域且其内容有可能会发生改变时,必须重写clone()方法为深拷贝
 * @author shijin
 *
 */
public class TestClone implements Cloneable{

	private Date date;
	private DeepCopy d;
	
	public Date getT() {
		return date;
	}
	
	public DeepCopy getD() {
		return d;
	}

	public void setT(Date date) {	
		this.date = date;
	}
	
	public void setD(DeepCopy d) {
		this.d = d;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj == null)
			return false;
		else {
			if(obj instanceof TestClone) {
				TestClone t = (TestClone)obj;
				if(this.date.equals(t.date) && this.d.equals(t.d))
					return true;
			}
			return false;
		}
		
	}

	/**
	 * 其实clone()的默认实现类似于函数函数中对date域的处理:field to field,浅拷贝,shallow copy
	 * 对于域中存在的非初始类型且其内容有可能发生变化的情况
	 * 就要改写clone()方法实现深拷贝deep copy,如函数中对d域的处理
	 * 不然拷贝对象与原对象不独立,两个对象的非初始类型域会共享内存空间
	 * 一个对象内存内容的改变会造成两个对象同步改变
	 * 
	 * 这里改变对象的内容与改变对象的引用不是一个概念
	 * field to field类型的复制只会造成两个相等的对象
	 * 对两个相等的对象,即为两个对象引用指向同一块内存空间
	 * 一个对象引用指向的改变不会影响另一个对象
	 * 只不过改变指向的对象指向了另一块内存,原指向断开被新的指向替代
	 * 未改变的对象依然指向原内存,两个对象不再相等
	 * 但是对象内容的改变会影响另一个对象,因为修改的是同一块内存
	 * 对此处的理解可以参考String变量的不可变性
	 * String类型的变量作为变量可以相继指向不同字符串常量
	 * 但对于一个固定的指向,其内存空间里的内容是不能改变的
	 * String s = new String("a");s ="a" + "b";两句话的内存分析
	 * s变量在栈内存
	 * new出来的String对象在堆内存
	 * "a"、"b"、"ab"三个字符串常量在数据段(常量池)
	 * s定义时指向堆内存中的String对象"a"
	 * 第二句执行后指向数据段中的字符串常量"ab"
	 * 内存中的内容不变,只是字符串变量的指向变了
	 * 			
	*/
	@Override
	public Object clone() throws CloneNotSupportedException {
//		TestClone t = (TestClone)super.clone();
//		t.setT(this.getT());
//		t.setD(this.getD());//浅拷贝
//		t.setD(new DeepCopy(this.d.getX()));//深拷贝
//		或者如下,如果DeepCopy实现了Cloneable接口的话,这里就有点递归的意思了
//		t.setD((DeepCopy)this.d.clone());
//		return t;
		
		return super.clone(); 

//		return new TestClone();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TestClone t = new TestClone();
		t.setT(new Date());
		t.setD(new DeepCopy(1));
		TestClone tClone = null;
		try {
			tClone = (TestClone)t.clone();
		} catch (CloneNotSupportedException e1) {
			e1.printStackTrace();
		}
		
//		这个!=是必须的,定义要求拷贝对象与原对象独立,不然就失去拷贝的意义了
		System.out.println("t " + ((t != tClone)?"!=":"==") + " t.clone()");
//		clone()中对d进行深拷贝时t.d != t.clone().d
		System.out.println("t.d " + ((t.d != tClone.d)?"!=":"==") + " t.clone().d");
//		clone()中对t进行浅拷贝t.date == t.clone().date
		System.out.println("t.date " + ((t.date != tClone.date)?"!=":"==") + " t.clone().date");
		
//		这个==虽然不是必须的,但是只要类及其父类clone()方法中的拷贝对象都是通过super.clone()获得
//		t.getClass() == tClone.getClass()就成立
		System.out.println("t.getClass() " + ((t.getClass() == tClone.getClass())?"==":"!=") + " tClone.getClass()");
		System.out.println("\tt = " + t);
		System.out.println("\ttClone = " + tClone);
//		即使clone()采用默认实现,仍然返回true,自动识别类型
//		Object中的clone执行的时候使用了RTTI(run-time type identification)的机制,即多态的实现机制
//		动态得找到目前正在调用clone方法的那个reference,根据它的大小申请内存空间
//		然后进行bitwise的复制,将该对象的内存空间完全复制到新的空间中去,从而达到shallow copy的目的。 
//		所以调用super.clone() 得到的是当前调用类的副本,而不是父类的副本。
		System.out.println("\ttClone 是不是TestClone的实例:" + (tClone instanceof TestClone));
		System.out.println("\tt.getClass() = " + t.getClass());
		System.out.println("\ttClone.getClass() = " + tClone.getClass());
		
//		这个==也不是必须的,比如在clone()和equals()都改写时
//		若对象中有一个域为unique,则该对象的克隆就可能与该对象不相等
		System.out.println("t " + ((t.equals(tClone))?"equales":"doesn't eaual") + " t.clone");
				
//		本段代码验证浅拷贝与深拷贝的区别		
		System.out.println("t.d.x改动前:" + t.d);
		System.out.println("tClone.d.x改动前:" + tClone.d);
		
		t.d.setX(2);
		
		System.out.println("t.d.x改动后:" + t.d);
		System.out.println("tClone.d.x改动前:" + tClone.d);

//		本段代码参考上面验证改变引用与改变内容的区别
//		对date的实验中直接更换date域的引用,而不是改变date的内容,所以体现不出浅拷贝的弊端
//		后期date域指向外部引用后两个对象也独立了
		System.out.println(t.date);
		System.out.println(tClone.date);
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		tClone.setT(new Date());
		
		System.out.println(t.date);
		System.out.println(tClone.date);	
	}
}
/**
 * 验证深拷贝的辅助类
 * @author shijin
 *
 */
class DeepCopy implements Cloneable{
	int x;
	
	public DeepCopy(int x) {
		this.x = x;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	@Override
	public String toString() {
		return x+"";
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		//成员为基本类型,不用改写方法
		return super.clone();
	}

	@Override
	public boolean equals(Object obj) {
		if(obj != null && obj instanceof DeepCopy) {
			DeepCopy d = (DeepCopy)obj;
			if(d.x == this.x)
				return true;
		}
		return false;
	}
}

你可能感兴趣的:(Date,String,object,null,equals,Class)