zoukankan      html  css  js  c++  java
  • 在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.
    仅此而已。
  • 相关阅读:
    Python day43 :pymysql模块/查询,插入,删除操作/SQL注入完全问题/事务/模拟登录注册服务器/视图/函数/存储过程
    docker
    Linux 05
    Linux04
    Linux 03
    Linux 02
    go语言
    go语言
    go语言
    Linux
  • 原文地址:https://www.cnblogs.com/daixianjun/p/java-Cloneable.html
Copyright © 2011-2022 走看看