前言

Cloneable是个标记接口,不包含任何的方法实现。如果对象需要clone,那么就需要对象实现cloneable接口,才能调用Object.clone()方法。

源码

  1. /**
  2. * 一个类实现了 Cloneable 接口,它就向 java.lang.Object#clone() 方法
  3. * 合法地标识了可以进行该类实例的字段拷贝。
  4. *
  5. * 未实现 Cloneable 接口就调用 Object's clone 方法时,将会抛出
  6. * CloneNotSupportedException 异常。
  7. *
  8. * 按照惯例,实现此接口的类应public声明去重写Object.clone 方法。
  9. * 查看{@link java.lang.Object#clone()}详情去覆盖这个方法。
  10. *
  11. * 注意这个接口不包含 clone 方法,因此,仅仅凭借实现这个接口并不能去克隆对象。
  12. * 即使这个clone方法被反射调用,也不能保证它会成功。
  13. */
  14. public interface Cloneable {
  15. }

标记接口

首先仅仅有个接口声明,但是并没有任何的实现,这个叫做Java Marker,即标记接口。比如JDK里的Serializable接口就是一个标记接口。
标记接口是任何语言都存在的,相当于类的元数据信息,表明了类的一些属性。
注:相比较而言java的注解(Annotation)更好,能够维护数据的元数据,而不用这样声明标记接口。但是java 1.5之后才有的,Cloneable接口是java 1.0就有的。

为什么这么设计?

综合下R大的回答就是如下:
任何对象都可以clone,同时可以手动标记哪些可以clone;最好JVM支持,等等。由于当时没有注解(JDK 1.5才有的)所以选择了标记接口方式,来当做类的元数据。

参考回答如下,详细见知乎RednaxelaFX回答

这是Java的一个设计缺陷。是个很糟糕的设计。如果Java在今天被重新设计一次的话,多半就不会设计成这样了。 当时的一些设计需求 / 限制是:

  • Java对象要支持clone功能。但不是所有Java对象都应该可以clone,而是要让用户自己标记出哪些类是可以clone的
  • clone()是一个特殊的多态操作,最好是有JVM的直接支持
  • 早期Java不支持annotation。从Java 5开始支持。
  • 早期Java支持接口形式的“声明多继承”
  • 早期Java不支持任何“实现多继承”(简称“多继承”)。从Java 8开始可以通过接口的default method实现。

把上述几条结合起来,就得到了Cloneable接口这个糟糕的设计。怎么说呢?首先,我们要能标记出哪些类是可以clone的。在Java里,类型层面的元数据可以用几种方法来表示:

继承的基类 实现的接口 直接在Class文件中通过access flags实现的修饰符 使用annotation,无论是自定义的还是Java自带的

显然当初设计Java的时候,一个类是否应该支持clone,是一个重要的属性,但却还没重要到值得给它一个关键字修饰符来修饰class声明,于是不能用(3)。然后Java类是单继承的,如果要出于标记目的而消耗掉“基类”这个资源,显然是有点别扭的(但想想看倒也不是完全不可以…),所以(1)也不太好。那么就只剩下(2)和(4)了。可是早期Java不支持(4),就只剩下(2)了。 其次,clone()的语义有特殊性,最好是有JVM的直接支持,然后用户代码就算要自定义clone()最好也要调用JVM提供的基础实现然后再添加自己的功能(也就是大家经常简单的在clone()中先调用super.clone()的做法)。JVM要直接支持,得在API里找地方来暴露出这个支持给Java代码调用才行啊。最直观的做法就是把clone()方法的基本实现放在一个所有可以clone的类都能访问到的基类中,让可clone的类继承这一实现。但根据上面一点的讨论,我们不希望把clone()的基本实现放在一个特殊基类中,消耗掉Java类唯一的“基类”槽。那还能放哪里呢?干脆就放在java.lang.Object这个所有Java类的共通基类上吧。

作者:RednaxelaFX

链接:https://www.zhihu.com/question/52490586/answer/130786763 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

参考