zoukankan      html  css  js  c++  java
  • 第11条:谨慎地覆盖clone

    Cloneable接口表明这样的对象时允许克隆的,但这个接口并没有成功达到这个目的,主要是因为它缺少一个clone方法,Object的clone方法是受保护的。如果不借助反射,就不能仅仅因为一个对象实现了Colneable就可以钓鱼clone方法,即使是反射调用也不能保证这个对象一定具有可访问clone方法。

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

    如果实现Cloneable接口是要对某个类起到作用,类和它的所有超类都必须遵守一个一定协议,言外之意就是无需调用构造器就可以创建对象。

    Clone它的通用约定非常弱:

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

    下面我们看下一个例子:

    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进行修改,如下所示即可

    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(); 

    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时,一定要分清需要克隆的对象属性。

    作者:哀&RT
    出处:博客园哀&RT的技术博客--http://www.cnblogs.com/Tony-Anne/
    您的支持是对博主最大的鼓励,感谢您的认真阅读。
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Mysql:Changes in MySQL 5.6.6 (2012-08-07, Milestone 9):Group-Commit等等:重大变化版本!
    Mysql:Changes in MySQL 5.6.9 (2012-12-11, Release Candidate):GTID-based variables have been 【renamed】
    Mysql:Changes in MySQL 5.6.13 (2013-07-31, General Availability):不再支持可能引起混乱的【选项缩略】写法!
    Mysql:Changes in MySQL 5.6.22 (2014-12-01, General Availability):【sql-log-bin】
    Mysql:Changes in MySQL 5.6.30 (2016-04-11, General Availability):--ssl-mode:
    Mysql:Changes in MySQL 5.6.34 (2016-10-12, General Availability):secure-file-priv
    Windows Linux子系统Windows 10安装指南
    WSL2-参考的对象类型不支持尝试的操作。
    Win10开启Hyper-V后无法运行VMware虚拟机的解决方法
    Kubernetes---高可用的 K8S 集群构建
  • 原文地址:https://www.cnblogs.com/Tony-Anne/p/6730468.html
Copyright © 2011-2022 走看看