- 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;
- }
- }