📜  Java中的克隆(1)

📅  最后修改于: 2023-12-03 15:31:58.032000             🧑  作者: Mango

Java中的克隆

在Java中,克隆即是创建一个与原始对象相同的新对象。Java中的克隆功能可以通过Cloneable接口实现。当一个对象调用clone()方法时,系统会创建一个新的对象,并复制原始对象的所有值,并将其作为新对象的属性进行设置,这种机制就称为克隆。

以下是克隆的基本实现方式:

public class MyClass implements Cloneable {
    
    private int value;
    
    public MyClass(int value) {
        this.value = value;
    }
    
    public void setValue(int value) {
        this.value = value;
    }
    
    public int getValue() {
        return value;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();   // 确保返回的是克隆的实例,否则抛出CloneNotSupportedException异常
    }
}

在Java中,Cloneable接口被用来标识可克隆的对象,如果一个对象没有实现Cloneable接口,调用它的clone()方法会抛出CloneNotSupportedException异常。同时,Object类中的clone()方法返回的是一个Object类型的对象,但使用时一般需要进行强制类型转换。

MyClass mc1 = new MyClass(10);
try {
    MyClass mc2 = (MyClass)mc1.clone();   // 通过强制类型转换实现克隆
    System.out.println("mc1: " + mc1.getValue());   // 输出 "mc1: 10"
    System.out.println("mc2: " + mc2.getValue());   // 输出 "mc2: 10"
    mc2.setValue(20);
    System.out.println("mc1: " + mc1.getValue());   // 输出 "mc1: 10"
    System.out.println("mc2: " + mc2.getValue());   // 输出 "mc2: 20"
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

从上述代码中可以看出,克隆后对象的属性与原始对象保持一致,并不会相互干扰。

注意:当对象是一个引用类型,则克隆得到的对象与原始对象的指针会指向同一引用对象,所以克隆后的对象改变了引用对象中的属性值,原始对象也会发生相应的改变。为了避免这种情况,需要将引用类型也进行克隆,确保克隆后的对象是新的对象,而不是原始对象的引用。

深克隆和浅克隆

以上是Java中的浅克隆,即只拷贝对象本身以及其数据成员的值,而对于它所引用的对象,只拷贝引用而不拷贝引用的对象本身。为了克服浅克隆的缺陷,需要使用深克隆,即在进行克隆时对所引用的对象也进行克隆。

以下是实现深克隆的一个例子(假设类MyClass中有一个成员变量为MyOtherClass):

public class MyClass implements Cloneable {
    
    private int value;
    private MyOtherClass myOtherClass;
    
    public MyClass(int value, MyOtherClass myOtherClass) {
        this.value = value;
        this.myOtherClass = myOtherClass;
    }
    
    public void setValue(int value) {
        this.value = value;
    }
    
    public int getValue() {
        return value;
    }
    
    public void setMyOtherClass(MyOtherClass myOtherClass) {
        this.myOtherClass = myOtherClass;
    }
    
    public MyOtherClass getMyOtherClass() {
        return myOtherClass;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        MyClass cloned = (MyClass)super.clone();
        cloned.myOtherClass = (MyOtherClass)myOtherClass.clone();   // 克隆对象的引用类型对象
        return cloned;
    }
}

在该实例中,对MyClass中的clone()方法进行重写,首先用super.clone()完成对本对象(即深克隆所在对象)的赋值,新建一个MyClass类的实例并将超类中的属性字段值复制到新的实例中,这样就可以实现深度克隆。接着将myOtherClass进行克隆,当MyClass类中含有多个引用类型时,需要对每个引用类型都进行克隆,同时保证克隆对象也会自动克隆它的引用对象,然后返回深克隆所在对象的实例。

总结

Java中的clone()方法用于创建新对象,使其属性与原始对象相同,但克隆对象与原始对象的引用地址不同。通过实现Cloneable接口,克隆对象较为容易。这里还介绍了深克隆和浅克隆的概念,当克隆对象中的成员包含引用类型时,需要进行深克隆,才能正确的克隆整个对象。

需要注意的是,使用克隆时需要避免对克隆对象进行不必要的修改,因为克隆后的对象仍然可能被其他对象引用。如果一定需要对克隆对象的属性进行修改,推荐使用深克隆的方式。