Java Object之clone()方法

Object中的clone方法定义如下:
protected native Object clone() throws CloneNotSupportedException;

定义一个可克隆的对象,一般的步骤如下:

  1. implements Cloneable;
  2. override clone()
Example
//Person及其子类都有了克隆能力
class Person implements Cloneable {
        private int age;
        private String name;

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

        public String getName() {
            return name;
        }

        @Override
        public Object clone() {
            try {
                //实际调用了Object的clone()
                return super.clone();
            } catch (CloneNotSupportedException e){
                e.printStackTrace();
                return null;
            }
        }

        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name.hashCode() + '\'' +
                    '}';
        }
    }

如果调用Person personOfClone = person.clone(),实际上进行的是 shallow copy,即person和personOfClone中的name引用的是同一块内存地址。对personOfClone的修改可能会影响到person。那么如何进行 deep copy?若要实现 deep copy,则必须对对象内部的引用进行clone(前提是这些引用是可克隆的)直到所有的引用都进行了clone。

默认浅克隆是合理的,因为不能确定所有的引用都是可以克隆的。

通过序列化实现 deep copy
Example
public class Compete {
    public static final int SIZE = 25000;

    public static void main(String[] args) throws Exception {
        Thing2[] a = new Thing2[SIZE];
        for (int i = 0; i < a.length; i++)
            a[i] = new Thing2();
        long t1 = System.currentTimeMillis();
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        ObjectOutputStream o = new ObjectOutputStream(buf);
        for (int i = 0; i < a.length; i++)
            o.writeObject(a[i]);
        // Now get copies:
        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));

        Thing2[] c = new Thing2[SIZE];
        for (int i = 0; i < c.length; i++)
            //这里实际上已经进行了copy
            c[i] = (Thing2) in.readObject();
        long t2 = System.currentTimeMillis();
        System.out.println("Duplication via serialization: " +
                (t2 - t1) + " Milliseconds");

        //clone
        Thing4[] b = new Thing4[SIZE];
        for (int i = 0; i < b.length; i++)
            b[i] = new Thing4();

        t1 = System.currentTimeMillis();
        Thing4[] d = new Thing4[SIZE];
        for (int i = 0; i < d.length; i++)
            d[i] = (Thing4) b[i].clone();
        t2 = System.currentTimeMillis();
        System.out.println("Duplication via cloning: " +
                (t2 - t1) + " Milliseconds");
    }
    static class Thing1 implements Serializable {
    }

    static class Thing2 implements Serializable {
        Thing1 o1 = new Thing1();
    }

    static class Thing3 implements Cloneable {
        public Object clone() {
            Object o = null;
            try {
                o = super.clone();
            } catch (CloneNotSupportedException e) {
                System.err.println("Thing3 can't clone");
            }
            return o;
        }
    }

    static class Thing4 implements Cloneable {
        private Thing3 o3 = new Thing3();

        public Object clone() {
            Thing4 o = null;
            try {
                o = (Thing4) super.clone();
            } catch (CloneNotSupportedException e) {
                System.err.println("Thing4 can't clone");
            }
            // Clone the field, too:
            o.o3 = (Thing3) o3.clone();
            return o;
        }
    }
}

运行后发现,直接clone的速度要远快于序列化

控制克隆能力

上述例子中,一个对象首先要实现Cloneable对象,然后覆写clone()才能具有克隆能力,那么为何这样设计?

  • 假如Object.clone()是public的
    实际上,java起初是这样设计的,Object中添加了public 的clone(),那么任何对象都可以克隆了,但是如果每个对象都是可以随意克隆的,就会出现安全性问题(你不一定愿意别人克隆你的对象);今天看到的样子,是做了许多修补之后的版本:Object 中的 clone()被声明为 protected,你必须重载它、实现 Cloneable 接口、并做异常处理
  • 假如Object.clone()是private的
    这样的话,子类都不能使用Object中的native方法了。

为何调用Object.clone()

调用Object.clone()时实际会发生什么,致使你重载clone()时必须要调用super.clone() 呢? 原因大概如下:

  • Object 类的 clone()方法负责创建正确容量的存储空间,并将属性值由原对象复制到新对象的存储空间中;
  • 此方法是native的,效率很高;
  • 可以防止不能克隆的对象进行克隆;此方法会在运行期间通过Cloneable对象判断对象是否可以克隆,如果不能克隆,则会抛异常。所以,若子类的方法没有调用Object.clone(),那么实际上是不用实现Cloneable接口的。但为了使具备克隆能力的对象保持一致性,可克隆的对象应该都实现该接口。

你可能感兴趣的:(Java Object之clone()方法)