clone是什么?
clone()是java提供一个功能,用于获取对象的一个副本。对于副本的的心理预期(并非必须)应该是[^1]:
- x.clone() != x 因为目标是对象,拷贝与原对象不应该是同一对象。
- x.clone().getClass() == x.getClass() 他们具有相同的类。
- x.clone().equals(x)==true 在比较时,它们应该是相等的。
clone()实质就是java就是对原型模式的原生支持。
clone()的实现
语法要求
Java提供了clone()方法的默认实现,在Object类中进行了声明:
protected native Object clone() throws CloneNotSupportedException;
首先可以看到它是native方法。Object.clone()执行时,会创建该类的新实例,并对所有字段进行浅拷贝: 基本类型字段复制值,对于引用类型字段只复制引用。
其次它是protected方法,要求子类如果需要提供该功能,必须覆盖该方法,并声明为public。
@override
public Type clone(){...}
最后可以看到其抛出CloneNotSupportedException异常,其要求子类如果提供了该功能,而且使用了Java默认实现(即调用了super.clone()),必须声明Cloneable标记接口。
虽然不使用默认实现时,不声明Cloneable也不会报错,但只要提供了clone()功能,就最好声明实现Cloneable。
public MyClass implements Cloneable{}
实现细节
参考Joshua Bloch的文章[^2][^4],实际要实现一个功能良好的clone()方法要考虑很多方面,而且存在其它更清晰的方式替代(如复制构造器)。
大概总结一下该文章(你最好亲自去看),就是:
- java语言对clone功能的设计是失败的。
- 合理的clone()内部实现,应该是一个clone()链条,通过层层super.clone(),最终到达Object.clone()。“类似构造器调用链”。
- 每一层都应该处理好实例域,包括需要更改的基本类型数据和需要深拷贝的引用类型数据。
- 要考虑线程安全、final字段的不可修改、异常处理等问题。
clone的应用
因为上述原因(Josua Bloch的文章),“应该尽量避免使用clone,clone显得有些笨拙”[^3]。从大神们的观点来看,除了数组的clone(),不要在其它地方使用clone[^4]。
实例场景
我对Spring Framework 的v4.0.2.RELEASE版本为样本,统计了Java代码实际应用中clone出现的次数:
正式代码
类型 | 次数 |
---|---|
java.util.Stack | 1 |
array | 14 |
WebClient.Builder | 1 |
WebHttpHandlerBuilder | 1 |
测试代码
类型 | 次数 |
---|---|
java.text.SimpleDateFormat | 1 |
java.util.LinkedHashMap | 2 |
LinkedCaseInsensitiveMap | 1 |
UriComponentsBuilder | 1 |
主要使用于在array和ADT这类数据集合,且很少被使用。
[^1] javadoc: Object.clone()
[^2] 《EffectiveJava》11谨慎覆盖clone
[^3]《java核心技术 卷1》6.2 对象克隆
[^4]Copy Constructor versus Cloning