美文网首页
java clone问题

java clone问题

作者: 三木仔 | 来源:发表于2017-04-06 12:54 被阅读248次

    在java中,如果需要有拷贝问题,都会使用到父类ObjectClone方法,能够为我们提供对象的拷贝方法,在使用过程中这里会有很多各式各样的问题。这里简单罗列一下个人所遇到的一些问题:

    1. 实现 Cloneable接口以及为何使用super.clone()

    Object的clone方法,就是拷贝一份副本出来,要求副本状态或者属性与原型要一致,但是相互要独立,改变原型或者副本,两者不会相互干扰。

     Object a = new Object();
     Object b = a;
    //虽然a和b相同,但是二者是指向同一个对象,所以不是克隆出来的
    
    • Object提供了clone方法,但是需要子类实现Cloneable接口,换句话说就是,只有实现Cloneable接口的类能被拷贝。

    • 在没有实现 Cloneable 接口的实例上调用Objectclone方法,则会导致抛出 CloneNotSupportedException异常。

    在子类覆盖了clone方法后,调用super.clone()方法,Object的clone()是一个native方法,会返回一个拷贝的实例。

    @Override
    public Person clone(){
          try{
                Person copy = (Person) super.clone();//返回一个拷贝的实例
          }catch(CloneNotSupportedException e){
                e.printStackTrace();
          }
    }
    

    值得注意的是,super.clone()只是进行浅拷贝,这一点会在下面进行讨论。

    使用super.clone()的好处是会检查是否实现了Cloneable,否则会抛异常,换句话说就是 ** 防止不可复制的类调用clone()方法**

    另外一种方法是提供拷贝构造器实现对象拷贝,好处就是构造器可以传递参数,而clone()却不可以。

    2. 深拷贝和浅拷贝问题

    浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象

    浅拷贝、深拷贝图:

    浅拷贝 深拷贝

    ** super.clone()默认是只进行浅拷贝的**

      class Person implements Cloneable{
              String name;
              Person parent;
              public Person( String name){
                  this.name =  name;
                  this.parent = null;
              }
      }
    
      Person father = new Person("father");  
      Person Jason = new Person("jason");
      Jason.parent = father;
      Person Bom = Jason.clone();//事实上,未覆盖的clone()方法调用的是Object的clone(),是浅复制
       
      //测试代码
      Bom.name = "Bom";
      Bom.parent.name = "mother";
      
      print(Bom.name);    // "Bom"
      print(Bom.parent.name);   // "mother"
      pirnt(Jason.name);   // " Jason"
      pirnt(Jason.parent.name);  // "mother" ,而不是"father"
      print(Bom == Jason); // "false",clone会生成一个新的对象
      print(Bom.parent == Jason.parent); // "true", 指向相同的对象
    

    除了不可变对象或者基本数据类型,java的对象复制只是对引用的复制,所以拷贝的副本和原型引用了同一个对象,因此根据业务需求,有时候我们需要深度拷贝:

    ** 在clone()中,可变的对象都需要进行重写clone(),保证不会引用到同一个对象,否则会造成各种问题。**

    class Person implements Cloneable{
          String name;
          Person parent;
          public Person( String name){
              this.name =  name;
              this.parent = null;
          }
    
          public Person clone(){
              Person copy = null;
              try{
                   copy = (Person) super.clone();
                   if(this.parent != null) 
                      copy.parent = this.parent.clone();
              } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
              }
              return copy;
          }
      }
    
    

    3. 在Iterator中注意的问题

    在Effective Java书中写道,clone就是另外一种构造器,你必须保证他不能伤害到原始的对象,并确保正确地创建被克隆。

    在java 容器中,拷贝的方法有:

    1. 直接通过构造器初始化,但是只是浅拷贝,拷贝的只是对象的引用
      List cloneList=new ArrayList(src);

    2. Collections.copy(List desc,List src) 方法,注意desc和src的size,不然会出现IndexOutOfBoundsException异常,这个方法同样是浅拷贝

    3. 通过迭代器循环逐个进行深度的clone()

    Clone方法就像构造方法,所以在里面最好只调用复制对象的final方法或者private方法,否则子类有可能重写该方法,这样子类在调用clone时会优先调用重写的方法,可能复制出来的对象不一致了。

    总结

    最后的原则:

    1. 首先调用super.clone(),然后修正需要改变的域。

    2. 要么写一个良好的clone()方法,否则就改用使用其他方法进行拷贝,因为clone会出现特别多意想之外的问题(大部分是深度和浅度复制问题),另外一种方法是直接不使用clone方法,而是采用拷贝构造器直接生成,自己修正需要改变的域,或者通过序列化的方法进行拷贝。

    3. 在clone方法中,一般通过super.clone()获取拷贝实例,再修改域,因此不能含有final变量,这是clone的另一个不足。

    参考

    相关文章

      网友评论

          本文标题:java clone问题

          本文链接:https://www.haomeiwen.com/subject/fxkoottx.html