Java中Clone()方法的子类实现问题:基于Cloneable接口的两种克隆方式
分析Cloneable接口下两种clone实现方式的差异及对子类的影响
嘿,我来帮你拆解这两种clone实现方式的核心区别,以及它们会给子类Student带来的不同影响:
实现方式1:手动实例化的自定义克隆
先看对应的代码:
@Override public Person clone() { return new Person(name, age); }
这种实现的本质是手动创建新对象并复制属性:
- 它直接调用Person的构造方法,把当前对象的
name和age传递进去完成属性复制。因为name是不可变的String类型、age是基本类型,所以这种方式的效果和浅克隆完全一致。 - 但如果Person后续新增了可变引用类型的属性(比如
List<String> hobbies),你必须手动在clone方法里复制这个属性(比如new ArrayList<>(this.hobbies)),否则克隆出的对象会和原对象共享同一个可变集合,修改其中一个会影响另一个。 - 对子类
Student来说,这种方式的复用性很差:Student要实现clone,必须自己手动创建Student实例,还要想办法获取父类的属性值(比如通过getter方法,或者把父类属性设为protected)来完成复制。举个例子:
public class Student extends Person { private String studentId; @Override public Student clone() { // 需要手动处理父类属性+子类属性 Student cloned = new Student(this.getName(), this.getAge(), this.studentId); return cloned; } }
实现方式2:基于Object.clone()的标准克隆
再看这种标准实现:
@Override public Person clone() { try { return (Person) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } }
这是Java中实现Cloneable接口的标准规范方式:
Object.clone()会自动创建一个和原对象类型相同的新实例,然后把原对象的所有属性值(包括基本类型的值、引用类型的地址)直接复制给新对象,也就是我们常说的浅克隆。- 因为Person已经实现了
Cloneable接口,所以super.clone()不会抛出CloneNotSupportedException(这里的异常捕获是防御性处理,避免后续代码变更导致的问题)。 - 对子类
Student来说,这种方式的扩展性极强:Student只需要也实现Cloneable接口,然后调用super.clone()获取父类克隆后的实例,再处理自己新增的属性即可。如果子类有可变引用类型的属性,只需要针对性地深克隆这些属性就行:
public class Student extends Person implements Cloneable { private String studentId; private List<String> courses; // 可变引用类型 @Override public Student clone() { try { // 复用父类的克隆逻辑,得到Student类型的实例 Student cloned = (Student) super.clone(); // 对可变属性进行深克隆,避免共享引用 cloned.courses = new ArrayList<>(this.courses); return cloned; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } }
两种方式的适用场景总结
- 如果你的Person类属性都是基本类型或不可变引用类型,两种方式效果相近,但方式2更符合Java的克隆规范,也更便于子类扩展。
- 如果Person包含可变引用类型的属性,方式2的代码更简洁:先通过
super.clone()完成浅克隆,再单独处理需要深克隆的属性即可;而方式1需要手动复制所有属性,容易遗漏。 - 方式1的灵活性更高:你可以在克隆时直接修改属性值(比如
return new Person(name, age + 1)),而方式2需要先克隆再修改。
内容的提问来源于stack exchange,提问作者Joyin
相关产品推荐
相关产品推荐

