缺少超克隆¶
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。