zoukankan      html  css  js  c++  java
  • java的clone()、浅拷贝与深拷贝

         clone()方法是Object的native方法。protected native Object clone() throws CloneNotSupportedException;  声明为protected,表明子类必须重新实现该方法,除非是与Obeject类在一个包里,后者是不可能的。而实际上,作为native方法clone()已经有一份field to field的浅拷贝实现,实际上是不需要一定重写的。这种情况下,需要的做法就是覆写clone()方法,在方法里通过super.clone()调用Object的clone()。

         而Cloneable是标记型接口,实现了Cloneable才可以实现clone()方法。否则使用clone()方法会报错。

         下面是ArrayList的clone()

        /**
         * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
         * elements themselves are not copied.)
         *
         * @return a clone of this <tt>ArrayList</tt> instance
         */
        public Object clone() {
            try {
                ArrayList<?> v = (ArrayList<?>) super.clone();
                v.elementData = Arrays.copyOf(elementData, size);
                v.modCount = 0;
                return v;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError(e);
            }
        }

         这里为何不是简单的super.clone()浅拷贝呢?因为成员变量是复杂类型时(涉及成员变量为对象的引用),就需要深拷贝。

         下面做个小实验,先使用浅拷贝,验证普通的成员变量是ok的:

    package a;
    
    public class CloneTest  implements Cloneable {
        private int v_a;
        public void setV_a(int v) {
            v_a = v;
        }
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        public void print_v_a() {
            System.out.println(v_a);
        }
        public static void main(String[] args) throws CloneNotSupportedException {
            CloneTest ct0 = new CloneTest();
            ct0.setV_a(66);
            CloneTest ct1 = (CloneTest)ct0.clone();
            ct1.print_v_a();
            ct0.setV_a(88);
            ct1.print_v_a();
            ct0.print_v_a();
        }
    }

         先设置ct0的v_a对象为66,然后ct1对象是ct0的拷贝,打印ct1的v_a,也为66,说明拷贝成功。 之后重新设置ct0的值为88,ct1的值没变还是66。

    package a;
    
    import java.util.Arrays;
    
    class A {
        private int v;
        public void setV(int v) {
            this.v = v;
        }
        public void p_v() {
            System.out.println(v);
        }
    }
    
    public class CloneTest  implements Cloneable {
        private A v_a;
        public void setV_a(A v) {
            v_a = v;
        }
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        public void print_v_a() {
            v_a.p_v();
        }
        public static void main(String[] args) throws CloneNotSupportedException {
            CloneTest ct0 = new CloneTest();
            A a = new A();
            a.setV(66);
            ct0.setV_a(a);
            CloneTest ct1 = (CloneTest)ct0.clone();
            ct1.print_v_a();
            a.setV(88);
            ct0.setV_a(a);
            ct1.print_v_a();
        }
    }

         上面这个例子就体现出了浅拷贝的弱点,输出为66 88。

         ct1是浅拷贝的ct0,此时ct0的v_a(成员变量,A对象)的v值为66。拷贝后,ct1的v_a(A对象的v)输出也是66。然后重设ct0的v_a(A对象的v)为88,再输出ct1的v_a(A对象的v),竟然也是88。说明二者的引用指向的是同样的堆内存。

         浅拷贝情况下,两个对象的成员变量(A对象)引用的是同一个堆内存,并没有完全实现拷贝后内存独立。

         这种情况就需要深拷贝。

         例如文章最开始提到的ArrayList的clone()的写法。

  • 相关阅读:
    九大经典算法之插入排序、希尔排序
    1072 开学寄语 (20 分)
    1070 结绳 (25 分
    查找字符串中的所有数字
    通过类继承计算梯形面积
    将命令的输出生成一个Web页面
    从Internet下载一个文件
    使用Excel管理命令输出
    将一个命令的输出保存到CSV文件
    使用属性存储用户编号和姓名
  • 原文地址:https://www.cnblogs.com/rixiang/p/6595652.html
Copyright © 2011-2022 走看看