zoukankan      html  css  js  c++  java
  • java克隆入门和深入

    我们知道,Java是纯面向对象的程序设计语言。Java里,所有的类的顶级父类都是java.lang.Object类,也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。
                                                                                       
       有一个很简单的方法可以证明这一点,我们写一个Test类,如下:
    public class Test {
        public void someMethod() {
            super.clone();
        }
    }
    里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。



    对象克隆
    本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。
    java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。
    首先我们看看下面的例子:

    public class TestClone {
        public static void main(String[] args) {
            MyClone myClone1 = new MyClone("clone1");
           
            MyClone myClone2 = (MyClone)myClone1.clone();
           
            if (myClone2 != null) {
                System.out.println(myClone2.getName());
                System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));
            } else {
                System.out.println("Clone Not Supported");
            }
        }
    }
    class MyClone {
        private String name;
        public MyClone(String name) {
            this.name = name;
        }
       
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
       
        public Object clone() {
            try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                return null;
            }
        }
    编译执行TestClone,打印出:

    C:\clone>javac *.java
    C:\clone>java TestClone
    Clone Not Supported
    C:\clone>

    说明MyClone#clone()方法调用super.clone()时抛出了CloneNotSupportedException异常,不 支持克隆。

    为什么父类java.lang.Object里提供了clone()方法,却不能调用呢?

    原来,Java语言虽然提供了这个方法,但考虑到安全问题, 一方面将clone()访问级别设置为protected型,以限制外部类访问;
    另一方面,强制需要提供clone功能的子类实现java.lang.Cloneable接口,在运行期,JVM会检查调用clone()方法的 类,如果该类未实现java.lang.Cloneable接口,则抛出CloneNotSupportedException异常。

    java.lang.Cloneable接口是一个空的接口,没有申明任何属性与方法。该接口只是告诉JVM,该接口的实现类需要开放“克隆”功 能。

    我们再将MyClone类稍作改变,让其实现Cloneable接口:
    class MyClone implements Cloneable {
        ...//其余不做改变
    }

    编译执行TestClone,打印出:

    C:\clone>javac *.java
    C:\clone>java TestClone
    clone1
    myClone2 equals myClone1: false
    C:\clone>

    根据结果,我们可以发现:
    1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象
    2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间)

    小结:
    如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。

    public class A extends Cloneable {
        public Object clone() {
            try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                //throw (new InternalError(e.getMessage()));
                return null;
            }
        }
    }


    对象的深层次克隆
      上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。


    但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的?
    很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。怎么样实现对象的深层次克隆呢?

    验证对象的克隆方式
    为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了setter与getter方法):

    public class TestClone {
        public static void main(String[] args) {
            //为克隆对象设置值
             MyClone myClone1 = new MyClone("clone1");
             myClone1.setBoolValue(true);
             myClone1.setIntValue(100);
           
            //设置List值
             List <Element>listValue = new ArrayList<Element>();
             listValue.add(new Element("ListElement1"));
             listValue.add(new Element("ListElement2"));
             listValue.add(new Element("ListElement3"));
             myClone1.setListValue(listValue);
           
            //设置Element值
             Element element1 = new Element("element1");
             myClone1.setElement(element1);
           
           
            //克隆
             MyClone myClone2 = (MyClone)myClone1.clone();
           
            if (myClone2 != null) {
               
                //简单属性
                 System.out.println("myClone2.name=" + myClone2.getName()
                         + " myClone2.boolValue=" + myClone2.isBoolValue()
                         + " myClone2.intValue=" + myClone2.getIntValue() );
               
                //复合属性(List<Element>与Element)
                 List clonedList = myClone2.getListValue();
                 Element element2 = myClone2.getElement();
               
                 System.out.println("myClone2.listValue.size():" + clonedList.size());
                 System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1));
                 System.out.println("myClone2.element.name:" + element2.getName());
               
                //下面我们测试一下myClone2.element是否等于myClone1.element
                //以及myClone2.listValue是否等于myClone1.listValue
                //为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,则它们引用 的是同一个内存空间的变量,我们认为它们相等
               
                 clonedList.add("ListElement4");
               
                 System.out.println("myClone1.listValue.size():" + listValue.size());
               
                 element2.setName("Element2");
                 System.out.println("myClone1.element.name:" + element1.getName());
               
             } else {
                 System.out.println("Clone Not Supported");
             }       
           
         }

    }


    class MyClone implements Cloneable {
        private int intValue;
        private boolean boolValue;
        private String name;
        private List <Element>listValue;
        private Element element;

        public MyClone(String name) {
             this.name = name;
         }

         ...//setter与getter方法(略)
    }

    class Element implements Cloneable   {
        private String name;
       
        public Element (String name) {
             this.name = name;
         }

         ...//setter与getter方法(略)
    }

    编译执行TestClone,打印出:

    C:\clone>javac *.java
    C:\clone>java TestClone
    myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100
    myClone2.listValue.size():3
    myClone2.element.equals(myClone1.element):true
    myClone2.element.name:element1
    myClone1.listValue.size():4
    myClone1.element.name:Element2
    myClone2 equals myClone1: false
    C:\clone>
    我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。也就是说,修改被克 隆后的对象值,会影响到原对象。

    怎么进行深层次的克隆呢?
    答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作

    class MyClone implements Cloneable {
         ...
        public Object clone() {
            try {
                 MyClone myClone = (MyClone)super.clone();
                //分别对属性加以克隆操作
                 myClone.element = this.element.clone();
               
                 myClone.listValue = new ArrayList();
                for (Element ele:this.listValue) {
                     myClone.listValue.add(ele.clone());
                 }
                           
                return myClone;
             } catch (CloneNotSupportedException e) {
                return null;
             }
         }
         ...
    }

    //让Element类也支持克隆操作
    class Element implements Cloneable   {
         ...
        public Element clone() {
            try {
                return (Element)super.clone();
             } catch (CloneNotSupportedException e) {
                return null;
             }
         }
    }

    深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。

    总结:
    本文结合范例,比较深入地介绍了Java语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。
  • 相关阅读:
    srum.3
    进程调度
    Scrum 项目——1
    《构建之法》—第6-7章
    团队合作——2
    团队合作——1
    作业调度_3
    作业调度——实验2
    典型用户
    【软件工程】做汉堡包
  • 原文地址:https://www.cnblogs.com/hdzsyp/p/1863020.html
Copyright © 2011-2022 走看看