为了实现克隆,我们需要配置我们的类并遵循以下步骤:
在我们的类或其超类或接口中实现 Cloneable 接口。
定义一个应处理 CloneNotSupportedException(抛出或记录)的 clone() 方法。
并且,在大多数情况下,我们从clone()方法中调用超类的clone()方法。
Java 克隆与复制构造函数
super.clone() 将调用它的 super.clone(),并且链将继续,直到调用到达 Object 类的 clone() 方法,该方法将创建一个字段到我们对象的字段 mem 副本并将其返回。
与一切事物一样,克隆也有其优点和缺点。然而,Java 克隆因其设计问题而闻名,但它仍然是当今最常见和流行的克隆策略。
Object.clone() 的优点
正如前面提到的,Object.clone() 存在许多设计问题,但它仍然是最流行和最简单的复制对象的方法。使用clone()的一些优点是:
克隆需要的代码行少得多——只需一个具有 4 行或 5 行长的 clone() 方法的抽象类,但如果我们需要深度克隆,我们将需要重写它。
这是复制对象的最简单方法,特别是当我们将其应用于已经开发的或旧的项目时。我们只需要定义一个父类,在其中实现 Cloneable,提供clone() 方法的定义,就可以了。我们父母的每个孩子都将获得克隆功能。
我们应该使用克隆来复制数组,因为这通常是最快的方法。
从版本 1.5 开始,在数组上调用克隆会返回一个数组,其编译时类型与被克隆的数组的编译时类型相同,这显然意味着在数组上调用克隆不需要类型转换。
Object.clone() 的缺点
以下是导致许多开发人员不使用 Object.clone() 的一些缺点:
使用 Object.clone() 方法需要我们在代码中添加大量语法,例如实现 Cloneable 接口、定义 Clone() 方法并处理 CloneNotSupportedException,最后调用 Object.clone() 并将其强制转换到我们的对象上。
Cloneable 接口缺少clone() 方法。实际上,Cloneable是一个标记接口,里面没有任何方法,我们仍然需要实现它只是为了告诉JVM我们可以对我们的对象执行clone()。
Object.clone() 是受保护的,因此我们必须提供自己的clone() 并从中间接调用Object.clone()。
我们无法控制对象构造,因为 Object.clone() 不会调用任何构造函数。
如果我们在子类(例如Person)中编写clone方法,那么它的所有超类都应该在其中定义clone()方法或从另一个父类继承它。否则, super.clone() 链将会失败。
Object.clone() 仅支持浅复制,因此新克隆的对象的引用字段仍将保留原始对象的字段所保留的对象。为了克服这个问题,我们需要在我们的类持有引用的每个类中实现clone(),然后在我们的clone()方法中单独调用它们的克隆,如下例所示。
我们无法在 Object.clone() 中操作 Final 字段,因为 Final 字段只能通过构造函数更改。在我们的例子中,如果我们希望每个 Person 对象的 id 都是唯一的,那么如果使用 Object.clone(),我们将得到重复的对象,因为 Object.clone() 不会调用构造函数,并且最终的 id 字段不能修改自 Person.clone()。