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

时间:2014-12-01 08:46:14

标签: java object clone native

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

protected native Object clone() throws CloneNotSupportedException;

为什么是native

4 个答案:

答案 0 :(得分:6)

查看克隆文档:

  

否则,此方法将创建此类的新实例   对象并初始化其所有字段的确切内容   该对象的相应字段,好像通过赋值;内容   这些田地本身并没有被克隆。

使用本机代码可以非常有效地完成此操作,因为必须直接复制某些内存。在这方面类似于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?

答案 1 :(得分:6)

基本上,因为clone()方法做了一些你不能用Java语言做的事情:它克隆了对象的状态,包括它的实际类名。

Java中的克隆机制基于调用超类clone方法的每个类,一直到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中所有内容的实现,因此需要调用A clone方法。但如果A在Java语言中实现clone而不是调用super.clone(),那么它将返回的对象必须是A。它不能使用new B()(假设在创建A时B未知)。

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

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

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

答案 2 :(得分:0)

clone()是本机的,因为操作取决于基础平台(也称为OS)。 以下是一些事实,可以帮助您了解实际发生的情况: 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
  1. 类方法是共享的=>不克隆;它们在内部用指针标识(一种方法不过是一组指令,所以指向该组,您只需要指向该组的第一条指令即可)
  2. 那么对象只是实例变量的特定状态
  3. 要克隆对象,方法clone()仅需要在内存中复制实例变量。看起来它是以字节为单位执行此操作的-与调用构造函数new SomeClassname()一样,没有进行任何分析-找出类在内存中的位置,构造函数在哪里,然后找出变量类型,并通过初始化值(如果有),并执行分配(如果有)。你明白了。
  4. 由于在给定类型的特定对象上调用了clone(),因此该类型本身是已知的,因此不需要上面(8)中的分析。甚至实例变量的值都相同,因此我们不需要传递任何内容。
  5. 剩下的唯一问题是DEEP副本-通过在每个依赖项对象上调用clone()并将引用分配给包装克隆中的实例变量来处理对依赖项的引用。

我知道这很长,但是很清楚地说明了为什么clone()比“ new”要快-只是将对象的状态捕获为二进制字符串并复制它而没有任何“ thought” /检查。

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

答案 3 :(得分:-1)

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