2025年克隆(拷贝)-java中的Cloneable接口和clone方法

克隆(拷贝)-java中的Cloneable接口和clone方法1 拷贝 克隆又称为拷贝 大家都知道克隆羊多莉吧 在 java 中 例如有一个对象 obj1 我想克隆出一个 obj2 这个 obj2 和 obj1 有着一样的内容 2 浅拷贝 Object 类提供了 protected 关键字修饰的 clone 方法 所有类都是 Object 类的子类

大家好,我是讯享网,很高兴认识大家。

1.拷贝

克隆又称为拷贝,大家都知道克隆羊多莉吧!在java中,例如有一个对象obj1,我想克隆出一个obj2,这个obj2和obj1有着一样的内容。

2..浅拷贝

Object类提供了protected关键字修饰的clone()方法,所有类都是Object类的子类,所以自定义类可以直接使用clone()方法拷贝一个初始状态和自己一样的对象。但是这样是存在局限性的。想想看Object类如何实现clone。它对于你自定义类的对象一无所知,所以只能逐个域的进行拷贝。如果对象中的所有数据域都是数值或者是其他基本类型,拷贝这些域没有任何问题。但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来原对象和克隆对象仍然会共享一些信息。

浅拷贝会有什么影响吗?这要看具体情况。如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的,如String就是这种情况。或者在对象的声明周期中,子对象一直包含不变的常量,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况下同样是安全的。不过,通常子对象都是可变的。必须重新定义cloned方法来建立一个深拷贝,同时克隆所有子对象。

3.深拷贝

之前我们提到不可变的对象,例如String类的对象,它是被final修饰的类,LocalTime也是如此。再来看看可变的情况,Date的实例就是可变的对象,所以它作为子类时,它也需要被克隆。

对于每一个类在克隆时,需要确定:

1)默认的clone()方法是否满足需求;

2)是否可以在可变的子对象上调用clone()方法来修补默认的clone()方法

如果满足以上任意一项,类必须:

1)实现Cloneable接口

2)重新定义clone方法,并指定pulic访问修饰符

4.实例


讯享网

import java.util.Date; ​ class Student{   public String name;     public Student(String name){       this.name = name;   } } ​ public class Test implements Cloneable{   public Student st;   public Date date;  //Date实现了clone方法     public Test(Date date) {   this.date=date;   }       /   * Test类重写clone方法   */    @Override protected Object clone() throws CloneNotSupportedException {   Test cloned=(Test) super.clone();     cloned.date=(Date) date.clone();   return cloned; } ​ ​ ​ public static void main(String[] args) throws CloneNotSupportedException{ Student  s1 = new Student("Tom");            Test t1 = new Test(new Date());            t1.st = s1;            Test t2 = (Test) t1.clone();                                    System.out.println("t1:\t"+t1);            System.out.println("t2:\t"+t2);            System.out.println("t1.st:\t"+ t1.st);            System.out.println("s1:\t"+s1);            System.out.println("t1.date:\t"+t1.date);            System.out.println("t2.date:\t"+t2.date);                        System.out.println("================================");                        System.out.println("t1 == t2\t"+(t1 == t2));            System.out.println("t1.equals(t2)\t"+(t1.equals(t2)));            System.out.println("t1.st != t2.s\t"+(t1.st != t2.st));            System.out.println("t1.st.equals(t2.st)\t"+t1.st.equals(t2.st));            System.out.println("t1.st==t2.st\t"+(t1.st==t2.st));            System.out.println("t1.date.equals(t2.date)\t"+(t1.date.equals(t2.date)));            System.out.println("t1.date==t2.date\t"+(t1.date==t2.date));                        new String("nihao");               } } 

讯享网

这是程序的输出情况

程序输出结果

 

让我们来模拟一下这个过程:

首先创建了Student类的对象s1,然后创建了Test类的t1,这里使用了当前系统时间去初始化Test的date属性。随后让t1对象的st引用了刚刚创建的s1所引用的域。因为现在t1.st和t1引用了同一个对象了,所以t1.st==st和t1.equals(t2))都为true。随后Test类创建了另一个对象,这个对象是由t1调用了Test类重写后的clone方法创建的。让我们来看看这个重写的方法做了什么:

讯享网/   * Test类重写clone方法   */    @Override protected Object clone() throws CloneNotSupportedException {   Test cloned=(Test) super.clone();     cloned.date=(Date) date.clone();   return cloned; }

它除了调用Object的clone方法之外,还对可变的对象data也进行了克隆。因为Data类也重写了clone方法,所以我们不用再考虑Data内部的子类是浅拷贝的情况了,它一定是一个深拷贝了。但是Test类不是还有一个属性st是自定义类Student吗?难道它不需要也像Data一样,由我们手动书写这个拷贝吗?别着急,这就是我为了对比写与不写的区别。

这是Test类的两个对象中的属性st(Student类)的对比情况。我们会发现,这两项的比较结果都是true。

 

这是Test类的两个对象中的属性date(Date类)的对比情况。我们会发现,使用euqals方法进行比较时,得到的结果是true,而使用全等号进行比较的时候,得到的结果却是false。

大家应该都知道,使用euqals方法进行比较相等就说明两个对象的值是相等的。而使用全等号==进行比较就说明这两个对象引用的是同一个对象!

由于我们在test重写的方法中没有对st属性也进行手动的克隆,所以这两个对象的st属性都是引用的同一个对象。而我们对Date进行了手动的克隆,这才是真正的会克隆一个date对象出来,而不仅仅只是引用了!

这就是为什么要实现Cloneable接口的原因!如果你要克隆的类里面有可变的类(你自定义的类大概率都是,除非你类里面只有不变的常量),那么这个类一定要实现Cloneable接口,在重写的clone方法里面,手动的去克隆可变类的对象,而且你一定要确保,这个可变类也重写了clone方法,当然,判断的方法和上面是一样的了!

小讯
上一篇 2025-03-20 16:59
下一篇 2025-03-19 12:28

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51159.html