为啥 Object.clone() 在 Java 中是原生的?

Posted

技术标签:

【中文标题】为啥 Object.clone() 在 Java 中是原生的?【英文标题】:Why is Object.clone() native in Java?为什么 Object.clone() 在 Java 中是原生的? 【发布时间】:2015-01-29 05:35:51 【问题描述】:

Object 上的 clone 方法创建对象的精确副本,声明为:

protected native Object clone() throws CloneNotSupportedException;

为什么是native

【问题讨论】:

请取消您的问题。 相关(重复?):***.com/a/557606/1225328 对于为什么 clone 是原生的,这个问题并没有确切的答案,只是某人的猜测。 同意。我已提名重新开放。 “什么是native”和“为什么将这个方法声明为native”之间存在巨大差异? 由于克隆不使用构造函数来创建对象的副本,并且由于 clone 方法通过调用 super.clone() 创建一个克隆,这将调用 Object 的 clone 方法,它 必须是原生的,才能创建 new 和相等的对象。 【参考方案1】:

基本上,因为clone() 方法做了一些您在Java 语言中无法做到的事情:它克隆对象的状态,包括其实际的类指定。

Java 中的克隆机制基于每个类调用超类的clone 方法,一直到Object。然后 Object 使用这个“神奇的”原生 clone 方法来复制原始对象,包括它的实际类。

想一想:

class A implements Cloneable 

    public A clone() 

        A obj = (A) super.clone();

        // Do some deep-copying of fields

        return obj;
    



class B extends A 

    public B clone() 

        B obj = (B) super.clone();

        // Do some deep-copying of fields not known to A

        return obj;

    

现在假设您有一个B 类型的对象,并在其上调用clone。您期望得到一个B 对象,其类在内部被识别为B,而不是ObjectB不知道A中所有内容的实现,因此需要调用Aclone方法。但是如果A 在Java 语言中实现clone 而不是调用super.clone(),那么它将返回的对象必须是A。它不能使用new B()(假设在创建 A 时 B 是未知的)。

它可以通过反射做一些事情,但它如何知道调用哪个构造函数以便正确填充所有最终字段?

所以诀窍是A 自己不会这样做,它会调用super.clone(),这会一直追溯到Object,它使用一个逐字节执行的本机方法复制原始对象,调整新的堆位置。因此,新对象神奇地变成了B 对象,并且类型转换不会失败。

那为什么不返回Object呢?因为那不会是克隆。当您调用clone 时,您希望得到一个具有相同状态(字段)和相同(被覆盖和添加的方法)的对象。如果它返回一个内部类指定为Object 的对象,您将只能访问Object 提供的内容,例如toString(),并且您将无法从另一个B 访问其私有字段对象,或将其分配给B 类型变量。

【讨论】:

Object.clone 进行浅拷贝,而不是深拷贝 @Ibalazscs true,但它在用户类中的实现通常必须自己进行深度复制。这就是为什么他们通常需要覆盖它而不是保持原样。 @ReakSkeptic 不确定答案最后一行中“从另一个 B 对象访问私有字段”是什么意思。无法使用克隆、子类化等方式协商私有字段。 @PrabhatGaur 如果你在B 类的源代码中并且你得到一个B 类型的对象并克隆它,你就可以访问它的所有私有字段。 @RealSkeptic "如果你在 B 类的源代码中" 你不需要克隆来访问这样一个对象的私有字段【参考方案2】:

查看克隆文档:

否则,此方法会创建 this 类的新实例 对象并完全使用对象的内容初始化其所有字段 这个对象的对应字段,就像通过赋值一样;内容 的字段本身没有被克隆。

使用本机代码可以非常有效地完成此操作,因为必须直接复制一些内存。在这方面它与System.arrayсopy 类似,后者也是原生的。详情看这个问题:Is it possible to find the source for a Java native method?

请注意,通常您应该避免使用 Object.clone(),而是使用例如复制构造函数,请参阅 How do I copy an object in Java?

【讨论】:

【参考方案3】:

它是原生的,因为一些系统类的Clone()方法是用C++编写的以提高性能。

【讨论】:

这如何回答这个问题? 这是抛出异常的原因 再次阅读问题。 好的。我认为我应该阅读所有不断变化的问题,并且应该根据当前问题更改答案。顺便说一句,这是原始问题的答案 这里有些混乱。最初的问题措辞不佳(现在仍然如此),而这个答案,在它的第一个版本中,实际上是无关紧要的。我相信情况不再如此。的确,这个答案远非完美,但请停止抨击。【参考方案4】:

clone() 是原生的,因为操作依赖于底层平台,也就是操作系统。 以下是一些有助于掌握实际情况的事实: 1.JVM用C++实现 2. C++要求你在目标平台/OS上编译代码 3. clone() 操作发生在内存中 4.那部分内存由JVM控制(一个C++程序) 5.一个类被编译成bytecode = text(忽略下面所有冗长的细节,它们只是为了说明) 所以,这个:

public static void main(String[] args) 
    int a = 1;
    int b = 2;
    int c = calc(a, b);

static int calc(int a, int b) 
    return (int) Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));

变成这样:

public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: (0x0009) ACC_PUBLIC, ACC_STATIC
  Code:
    stack=2, locals=4, args_size=1
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: invokestatic  #2         // Method calc:(II)I
       9: istore_3
      10: return
static int calc(int, int);
  descriptor: (II)I
  flags: (0x0008) ACC_STATIC
  Code:
    stack=6, locals=2, args_size=2
       0: iload_0
       1: i2d
       2: ldc2_w        #3         // double 2.0d
       5: invokestatic  #5         // Method java/lang/Math.pow:(DD)D
       8: iload_1
       9: i2d
      10: ldc2_w        #3         // double 2.0d
      13: invokestatic  #5         // Method java/lang/Math.pow:(DD)D
      16: dadd
      17: invokestatic  #6         // Method java/lang/Math.sqrt:(D)D
      20: d2i
      21: ireturn
    类方法是共享的 => 不是克隆的;它们在内部用指针标识(方法无非是一组指令,因此要指向该分组,您只需指向该分组的第一条指令) 对象只是实例变量的特定状态 要克隆一个对象,clone() 方法只需要复制内存中的实例变量。它似乎以字节块的形式执行此操作 - 没有像调用构造函数 new SomeClassname() 那样进行分析 - 找出类在内存中的位置,构造函数在哪里,然后找出变量类型,传递初始化值(如果有),并执行分配(如果有)。你明白了。 由于对给定类型的特定对象调用 clone(),因此类型本身是已知的,不需要上面 (8) 中的分析。甚至实例变量的值都是一样的,所以我们不需要传递任何东西。 剩下的唯一问题是 DEEP 副本 - 通过在每个依赖项对象上调用 clone() 并将引用分配给包装器克隆中的实例变量来处理对依赖项的引用。

我知道这很长,但它非常清楚地说明了为什么 clone() 比“new”更快 - 只是将对象的状态作为二进制字符串抓取并复制它而无需任何“思考”/检查。

要了解有关字节码的更多信息,请查看this article in DZone。 请记住....计算机科学中的一切都是假的,包括类对象,因为它们只是将相关结构和函数组合在一起的另一种抽象,可能除了硬件:),大多数人实际上认为属于计算机工程,而不是 CS。

【讨论】:

以上是关于为啥 Object.clone() 在 Java 中是原生的?的主要内容,如果未能解决你的问题,请参考以下文章

为什么Object.clone()是Java原生的?

Java Object Clone

在java中,如何复制一个对象?比如说string对象

Java克隆(Clone)的应用

Object.clone() 的逐个字段复制是啥?

java对象的内存布局