📜  Java对象clone()(1)

📅  最后修改于: 2023-12-03 14:43:00.767000             🧑  作者: Mango

Java对象克隆(clone)

Java克隆技术可以将一个Java对象的状态(即其所有的字段值)复制到另外一个Java对象中。在Java中,对象克隆使用clone()方法,这个方法是从Object类继承下来的:

public class Object {
    protected native Object clone() throws CloneNotSupportedException;
}

为了使用clone()方法,你的Java对象必须实现Cloneable接口。该接口指定该对象允许克隆。如果尝试克隆一个未实现Cloneable接口的Java对象,会引发CloneNotSupportedException异常。

在Java中,对象克隆有两种方式,即"浅克隆"和"深克隆"。

浅克隆(Shallow Clone)

浅克隆是指只克隆Java对象的字段值,并不克隆嵌套的对象。如果克隆一个对象,对于原始对象中的引用类型字段,它们的引用地址将被共享。

下面是一个例子:

class Person implements Cloneable {
    public String name;
    public int age;
    public Address address;

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

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Address {
    public String city;

    public Address(String city) {
        this.city = city;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("Beijing");
        Person p1 = new Person("Tom", 20, addr);
        Person p2 = (Person) p1.clone();

        System.out.println(p1.name + ", " + p1.age + ", " + p1.address.city);
        System.out.println(p2.name + ", " + p2.age + ", " + p2.address.city);

        addr.city = "Shanghai";

        System.out.println(p1.address.city); //输出"Shanghai"
        System.out.println(p2.address.city); //输出"Shanghai"
    }
}

注意到Person对象中包含了一个Address对象。在clone()方法中,我们调用了super.clone()来克隆Person对象。这意味着我们会获得一个新的Person对象,其中包含与原始对象相同的值。

address字段中,我们声明了一个指向Address对象的引用。当我们更新Address对象中的city字段时,我们发现这个改变同时反映在了p1p2中。

Tom, 20, Beijing
Tom, 20, Beijing
Shanghai
Shanghai

因为在浅克隆中,克隆的是引用地址,所以address字段实际上是Person对象和p2对象所共享的。所以在改变addr对象中的city字段时,这个改变也被反映在p1p2中。

深克隆(Deep Clone)

深克隆是指克隆整个Java对象,包括其字段中的对象。这种克隆方式将创建一个对象副本,其中包含与原始对象不同的内存地址。

和浅克隆不同的是,深克隆中对于原始对象中的对象字段,会创建一个新对象进行复制,而不是共享引用。

下面是一个例子:

import java.io.*;

class Person implements Cloneable, Serializable {
    public String name;
    public int age;
    public Address address;

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

    @Override
    public Object clone() throws CloneNotSupportedException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        try {
            return ois.readObject();
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}

class Address implements Serializable {
    public String city;

    public Address(String city) {
        this.city = city;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("Beijing");
        Person p1 = new Person("Tom", 20, addr);
        Person p2 = (Person) p1.clone();

        System.out.println(p1.name + ", " + p1.age + ", " + p1.address.city);
        System.out.println(p2.name + ", " + p2.age + ", " + p2.address.city);

        addr.city = "Shanghai";

        System.out.println(p1.address.city); //输出"Shanghai"
        System.out.println(p2.address.city); //输出"Beijing"
    }
}

在这个例子中,我们使用了Java序列化机制来实现深克隆。当我们调用clone()方法时,它会将对象序列化到一个字节数组中。然后再将其反序列化到一个新的Person对象中,这样就得到了一个完整的深度克隆对象。

注意到Address类必须也要实现序列化接口才能深度克隆Person对象。

在这个例子中,我们可以看到浅克隆和深克隆的不同点。由于深克隆中所有的对象都会被复制,所以当我们改变addr对象中的city字段时,只有原始对象中的address字段被修改了,而复制出来的p2对象中的address字段并没有被修改。

Tom, 20, Beijing
Tom, 20, Beijing
Shanghai
Beijing

总结:Java对象克隆技术是一种很有用的特性,可以通过这种技术方便的创建一个新的对象。对于一些特定业务场景,我们可以通过实现Cloneable接口或者序列化接口来实现浅克隆或者深克隆。