缺少超克隆¶
ID: java/missing-call-to-super-clone
Kind: problem
Security severity:
Severity: error
Precision: medium
Tags:
- reliability
- maintainability
- external/cwe/cwe-580
Query suites:
- java-security-and-quality.qls
在子类中重写的 clone 方法应调用 super.clone。如果不这样做,子类 clone 将返回错误类型的对象,这违反了 Cloneable 的约定。
Java API 规范指出,对于对象 x,clone 方法的一般目的是满足以下三个属性
x.clone() != x(克隆对象是不同的对象实例)x.clone().getClass() == x.getClass()(克隆对象与源对象类型相同)x.clone().equals(x)(克隆对象与源对象“内容”相同)要使克隆对象与源对象类型相同,非 final 类必须调用super.clone,并且该调用最终必须到达Object.clone,它创建一个正确类型的实例。如果它使用构造函数创建一个新对象,则未实现clone方法的子类将返回错误类型的对象。此外,所有也重写clone的类的超类型都必须调用super.clone。否则,它永远不会到达Object.clone,并创建一个类型不正确的对象。
但是,由于 Object.clone 仅对对象的字段进行浅层复制,因此任何具有“深层结构”的 Cloneable 对象(例如,使用数组或 Collection 的对象)都必须获取从调用 super.clone 生成的克隆,并将显式创建的结构副本分配给克隆的字段。这意味着克隆实例不与其源对象共享其内部状态。如果它确实共享其内部状态,则在克隆对象中进行的任何更改也会影响源对象的内部状态,这可能会导致意外的行为。
另一个复杂之处在于 clone 无法修改 final 字段中的值,这些值已由调用 super.clone 设置。必须将某些字段设为非 final 才能正确实现 clone 方法。
建议¶
每个克隆方法都应该始终使用 super.clone 来构造克隆对象。这确保克隆对象最终由 Object.clone 构造,后者使用反射来确保创建正确运行时类型的对象。
示例¶
在以下示例中,尝试克隆 WrongEmployee 失败,因为 super.clone 在其超类 WrongPerson 中实现不正确。
class WrongPerson implements Cloneable {
private String name;
public WrongPerson(String name) { this.name = name; }
// BAD: 'clone' does not call 'super.clone'.
public WrongPerson clone() {
return new WrongPerson(this.name);
}
}
class WrongEmployee extends WrongPerson {
public WrongEmployee(String name) {
super(name);
}
// ALMOST RIGHT: 'clone' correctly calls 'super.clone',
// but 'super.clone' is implemented incorrectly.
public WrongEmployee clone() {
return (WrongEmployee)super.clone();
}
}
public class MissingCallToSuperClone {
public static void main(String[] args) {
WrongEmployee e = new WrongEmployee("John Doe");
WrongEmployee eclone = e.clone(); // Causes a ClassCastException
}
}
然而,在以下修改后的示例中,尝试克隆 Employee 成功,因为 super.clone 在其超类 Person 中实现正确。
class Person implements Cloneable {
private String name;
public Person(String name) { this.name = name; }
// GOOD: 'clone' correctly calls 'super.clone'
public Person clone() {
try {
return (Person)super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError("Should never happen");
}
}
}
class Employee extends Person {
public Employee(String name) {
super(name);
}
// GOOD: 'clone' correctly calls 'super.clone'
public Employee clone() {
return (Employee)super.clone();
}
}
public class MissingCallToSuperClone {
public static void main(String[] args) {
Employee e2 = new Employee("Jane Doe");
Employee e2clone = e2.clone(); // 'clone' correctly returns an object of type 'Employee'
}
}
参考¶
J. Bloch,Effective Java(第二版),第 11 项。Addison-Wesley,2008 年。
Java API 规范:Object.clone()。
通用弱点枚举:CWE-580。