第11条:谨慎地覆盖clone

Cloneable接口代表这样的对象时容许克隆的,但这个接口并无成功达到这个目的,主要是由于它缺乏一个clone方法,Object的clone方法是受保护的。若是不借助反射,就不能仅仅由于一个对象实现了Colneable就能够钓鱼clone方法,即便是反射调用也不能保证这个对象必定具备可访问clone方法。数据结构

既然Cloneable并无包含任何方法,那么它到底有什么用呢?它其实以为了Object中受保护的clone方法实现的行为,若是一个类实现了Cloneable那么Object的clone方法就返回该对象的逐域拷贝,不然会抛出CloneNotSupportedException。但真说接口一种极端非典型用法,不值得提倡。this

若是实现Cloneable接口是要对某个类起到做用,类和它的全部超类都必须遵照一个必定协议,言外之意就是无需调用构造器就能够建立对象。spa

Clone它的通用约定很是弱:code

  建立和返回该对象的一个拷贝。这个拷贝的精确含义取决于该对象的类。通常含义是,对于任何对象x,表达式x.clone() != x 将会是true,而且,表达式x.clone().getClass() == x.getClass() 将会是true,但这些不是绝对的要求,一般状况下,表达式x.clone().equals(x) 将会是true,这也不是一个绝对的要求,拷贝对象每每是建立它的类的一个新实例,但它同时也会要求拷贝内部的数据结构。对象

下面咱们看下一个例子:blog

public class Student implements Cloneable{
    String name;
    int age;
    
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public Object clone(){
        Object o = null;
        try{
             o = (Student)super.clone();//Object 中的clone()识别出你要复制的是哪一个对象    
        }catch(CloneNotSupportedException e){
             System.out.println(e.toString()); 
        }
        return o; 
    }

    public static void main(String[] args){ 
            Student s1=new Student("zhangsan",18); 
            Student s2=(Student)s1.clone(); 
            System.out.println("克隆后s2:name="+s2.name+","+"age="+s2.age); 
            s2.name="lisi"; 
            s2.age=20; 
            //修改学生2后,不影响学生1的值。
            System.out.println("克隆修改后s1:name="+s1.name+","+"age="+s1.age); 
            System.out.println("克隆修改后s2:name="+s2.name+","+"age="+s2.age);
        }
}

这时候,若是类的每一个域包含一个基本类型的值,或者包含一个指向不可变对象的引用,那么被返回的对象则正是所须要的对象,只须要简单地调用super.clone() 而不用作进一步的处理。可是!若是对象中其余对象的引用时,那么只是简单的clone就没法作到彻底的克隆了,下面的例子咱们就能够体会到接口

class Professor { 
    String name; 
    int age; 
    Professor(String name,int age){ 
        this.name=name; 
        this.age=age; 
    } 
} 

public class Student implements Cloneable{ 
    String name;// 常量对象。 
    int age; 
    Professor p;// 学生1和学生2的引用值都是同样的。 

    Student(String name,int age,Professor p){ 
        this.name=name; 
        this.age=age; 
        this.p=p; 
    } 

    public Object clone(){ 
        Student o=null; 
        try{ 
                o=(Student)super.clone(); 
        }catch(CloneNotSupportedException e){ 
                System.out.println(e.toString()); 
        } 
        
        return o; 
    } 

    public static void main(String[] args){ 
          Professor p=new Professor("wangwu",50); 
          Student s1=new Student("zhangsan",18,p); 
          Student s2=(Student)s1.clone(); 
          System.out.println("克隆后s1:name="+s1.p.name+","+"age="+s1.p.age);
          System.out.println("克隆后s2:name="+s2.p.name+","+"age="+s2.p.age);
          s2.p.name="lisi"; 
          s2.p.age=30;  
          System.out.println("克隆后s1:name="+s1.p.name+","+"age="+s1.p.age);
          System.out.println("克隆后s2:name="+s2.p.name+","+"age="+s2.p.age);
    } 
}

从结果上咱们能够看出,s2对s1进行克隆时,对s1的属性Professor p并无进行克隆,致使s1和s2对其引用指向同一个,这会形成s2若改变了值,s1则也被动改变了。那应该如何实现深层次的克隆,即修改s2的教授不会影响s1的教授?其实很简单,只须要对Professor进行修改,以下所示便可get

class Professor  implements Cloneable{ 
            String name; 
            int age; 
            Professor(String name,int age){ 
                this.name=name; 
                this.age=age; 
            } 
    
            public Object clone(){
                Object o = null;
                try{ 
                    o = super.clone(); 
                }catch(CloneNotSupportedException e){ 
                    System.out.println(e.toString()); 
                } 
                return o; 
            }
        }

修改Professor后,还须要在Student的clone方法中加入一句代码:o.p=(Professor)p.clone(); io

public Object clone(){ 
        Student o=null; 
        try{ 
                o=(Student)super.clone(); 
        }catch(CloneNotSupportedException e){ 
                System.out.println(e.toString()); 
        } 
       o.p=(Professor)p.clone(); 
        return o; 
} 

看到结果就如咱们所但愿的那样。所以,在使用clone时,必定要分清须要克隆的对象属性。class

相关文章
相关标签/搜索