在Java中为什么实现了Cloneable接口,就能调用Object的clone方法?

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

这是Java的一个设计缺陷。是个很糟糕的设计。如果Java在今天被重新设计一次的话,多半就不会设计成这样了。

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

把上述几条结合起来,就得到了Cloneable接口这个糟糕的设计。

怎么说呢?

首先,我们要能标记出哪些类是可以clone的。在Java里,类型层面的元数据可以用几种方法来表示:
  1. 继承的基类
  2. 实现的接口
  3. 直接在Class文件中通过access flags实现的修饰符
  4. 使用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类的共通基类上吧。

诶。

=======================================

所以说一个实现了Cloneable接口的类跟一个没实现该接口的类有啥区别呢?
从JVM的角度看,这就是一个标记接口而已。实现了就是打上cloneable标记,没实现就是没这个标记。
然后到clone()的基本实现中,JVM会去检测要clone的对象的类有没有被打上这个标记,有就让clone,没有就抛异常。就这么简单。

Java里的数组类型是由JVM直接实现的,没有对应的Java源码文件。具体JVM如何实现是它们的自由。
Java语言规范所做的规定仅有这么一节:Chapter 10. Arrays
引用其中的演示用代码:
An array thus has the same public fields and methods as the following class:
class A<T> implements Cloneable, java.io.Serializable {
    public final int length = X;
    public T[] clone() {
        try {
            return (T[])super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}
Note that the cast to T[] in the code above would generate an unchecked warning (§5.1.9) if arrays were really implemented this way.
仅此而已。
原文地址:https://www.cnblogs.com/daixianjun/p/java-Cloneable.html