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语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。
  • 相关阅读:
    HDU 2888 Check Corners (模板题)【二维RMQ】
    POJ 3264 Balanced Lineup(模板题)【RMQ】
    poj 3368 Frequent values(经典)【RMQ】
    SPOJ RPLN (模板题)(ST算法)【RMQ】
    UVA 796 Critical Links(模板题)(无向图求桥)
    UVA 315 Network (模板题)(无向图求割点)
    POJ 2029 Get Many Persimmon Trees (模板题)【二维树状数组】
    poj 3067 Japan 【树状数组】
    POJ 2481 Cows 【树状数组】
    POJ 1195 Mobile phones【二维树状数组】
  • 原文地址:https://www.cnblogs.com/hdzsyp/p/1863020.html
Copyright © 2011-2022 走看看