java clone的思考

clone是什么?

在学习技术时,我们首先需要知道这个东西的是什么,有什么用,最佳的学习方式是基于需求驱动的学习。
对于clone(),它是java提供一个特性,用于获取对象的一个副本。对于副本的的心理预期(并非必须)应该是[^1]:

  • x.clone() != x 因为目标是对象,拷贝与原对象不应该是同一对象。
  • x.clone().getClass() == x.getClass() 他们具有相同的类。
  • x.clone().equals(x)==true 在比较时,它们应该是相等的。

以上就是clone()应该对外提供的功能,以及使用该功能的心理预期。可以看到,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{}

实现细节

实现时主要从以下几点来考虑[^2]:

  • Java默认实现能否满足需求
  • 在默认实现上进行修补是否能满足需求
  • 应该提供浅拷贝还是深拷贝

clone的应用

Java虽然提供了clone特性,但在实践过程中,clone很少被使用,而且“应该尽量避免使用clone,clone显得有些笨拙”[^2]。
在我看来,clone使用得少是有原因的,我也赞成不推荐使用clone。

  • 首先clone拷贝目标是对象,但许多人滥用于拷贝数据。对象虽然维护了数据,但还有其它许多状态字段,而此时用户关注的往往是数据而非对象,因此因为复制数据而使用clone会造成用户的关注点转移到了对象本身。
  • 其次clone的功能是对对象进行拷贝,而实现细节:浅拷贝、深拷贝、状态字段如何处理...是与具体的类有关,而且还与继承层次有关,使用clone会加重开发者和用户的思考负担

因为clone()会导致用户的关注点转移和加重开发者和用户的思考负担,因此一般不建议使用clone(),而推荐使用更清晰和轻量的接口。

数组的clone

数组的clone()是Java中为数不多的应用广泛的clone特性,它返回数组的拷贝,使用默认的浅拷贝方式。因为数组没有什么状态,因此其等同于复制数据,但相比Arrays.copyOf()在写法上更简单。

List,Map等ADT的clone

不推荐使用。在使用这些ADT的时候,我们的关注点应该是数据本身,应当按照下面的方式来复制ADT的数据:

new ArrayList<>(origin)
new HashMap<>(origin)

当然,从效果上来讲,使用这些ADT的clone()也不会影响程序的正确性。

实例场景

我对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

可以看到clone的使用场景主要是在array和ADT这类数据集合的场景。而对于其中使用到的自定义类型的clone,我发现其实现往往很简单:

public HandlerMethodParameter clone() {     
    return new HandlerMethodParameter(this);
}

[^1] javadoc: Object.clone()
[^2]《java核心技术 卷1》6.2 对象克隆

猜你喜欢

转载自www.cnblogs.com/redreampt/p/9418960.html