详解Java的clone()方法:创建并返回此对象的一个副本

  • Post category:Java

Java 中的 clone() 方法是一种创建对象副本的方式,它是 Cloneable 接口的一部分,其作用是复制原始对象的内容并创建一个新的对象。

一、clone() 方法的语法与用法

clone() 方法的语法如下:

public Object clone() throws CloneNotSupportedException

clone() 方法可以复制任何一个实现了 Cloneable 接口的对象,但是如果这个对象没有实现 Cloneable 接口,那么将会抛出 CloneNotSupportedException 异常。在使用 clone() 方法时,需要注意以下几点:

  1. 实现 Cloneable 接口:为了让一个类的实例能被 clone() 复制,必须实现 Cloneable 接口,并重写它的 clone() 方法。

  2. 重写 clone() 方法:因为 Object 类的 clone() 方法是 protected 访问级别的,所以需要重写 clone() 方法以改变其访问级别为 public,并声明为抛出 CloneNotSupportedException 异常。

  3. 原对象与克隆对象不同:clone() 方法复制出来的新对象和原对象不是同一个对象。如果原对象发生变化,不会影响到新的克隆对象。同时,克隆对象和原对象所在的类类型相同,但是对象引用都是独立的,对克隆后的对象进行更改不会对原始对象产生影响。

下面是一个最简单的示例,将 Car 类实现 clone() 方法:

public class Car implements Cloneable {
    private String name;
    private int num;

    public Car(String name, int num) {
        this.name = name;
        this.num = num;
    }

    // 重写 cloning 方法
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public int getNum() {
        return num;
    }
}

在这个代码里,我们不仅实现了 Cloneable 接口,还重写了 clone() 方法,并使用了 super.clone() 方法来获得一个新的复制对象。

二、示例说明

示例一:复制数组

接下来,为了更好地说明 clone() 方法,我们使用一个包含多个 Car 对象的数组作为案例。 我们可以创建一个数组 cars,并将实例存储在 cars 数组中:

public class CloneApplication {
    public static void main(String[] args) {
        Car[] cars = {new Car("Bob", 25), new Car("Tom", 22)};
        // 使用 clone() 方法复制数组
        Car[] cloneCars = cars.clone();

        // 比较两个数组是否相等
        System.out.println(cars == cloneCars); // false
        System.out.println(cars[0] == cloneCars[0]); // true
        System.out.println(cars[1] == cloneCars[1]); // true

        // 修改复制的对象。
        cloneCars[0].setName("Jack");
        cloneCars[0].setNum(20);

        // 只是因为车的容器有所不同,所以它们不相等。
        System.out.println(cars[0].getNmae()); // Bob
        System.out.println(cloneCars[0].getNmae()); // Jack
    }
}

在这个示例里,我们定义了一个数组 cars,然后使用 clone() 方法复制 cars,最后修改 cloneCars 中的一个对象,从输出结果来看,两个数组不相等,并且 cloneCars 的第一个元素经过修改,原始数组 cars 的第一个元素不受影响。

示例二:实现深度复制

现在,让我们创建一个包含复杂对象图的类,来演示如何将对象图复制到新对象中。 假设我们要将一个带有内部类的 Student 对象进行复制,Student 类通常看起来会像这样:

public class Student implements Cloneable {
    private String name;
    private int age;
    private Books books;

    public Student(String name, int age, Books books) {
        this.name = name;
        this.age = age;
        this.books = books;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student cloned = (Student) super.clone();
        cloned.setBooks((Books) cloned.getBooks().clone());
        return cloned;
    }

    // 将getter,setter省略
}

在这个示例里,我们使用另一个类 Books 来存储一系列书籍,这个类也实现了 clone() 方法。 在 Student 类中,我们先调用 super.clone() 复制基本属性,然后将类的引用类型复制到新的对象中。 在这种情况下,我们需要保证 books 引用类型而不是修改原始对象。

现在,我们就可以使用这个类创建对象,并将其复制到一个新对象中:

public class CloneApplication {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student = new Student("Tom", 17, new Books());
        Student clonedStudent = (Student) student.clone();

        System.out.println(student.getName()); // Tom
        System.out.println(clonedStudent.getName()); // Tom

        // 修改克隆的对象
        clonedStudent.setName("Alice");
        clonedStudent.getBooks().setName("English");

        // 只有克隆的对象改变了
        System.out.println(student.getName()); // Tom
        System.out.println(clonedStudent.getName()); // Alice
        System.out.println(student.getBooks().getName()); // null
        System.out.println(clonedStudent.getBooks().getName()); // English
    }
}

可以看到,我们成功地创建了一个新的对象,并且修改了其中的一个成员变量,然而原始对象并没有发生任何变化。

结语

有了 clone() 方法,您可以轻松地复制对象,并且新的复制对象具有原始对象的所有属性。 我们需要注意的是,被 clone() 方法复制出来的对象和原对象是两个不同的对象,对其中一个对象做出的改变并不会影响另一个对象。