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

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

 protected native Object clone() throws CloneNotSupportedException; 

为什么它是native

基本上,因为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)。

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

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

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

查看克隆文档:

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

使用本机代码可以非常有效地完成此操作,因为必须直接复制某些内存。 在这方面类似于System.arrayсopy ,它也是原生的。 有关详细信息,请参阅此问题: 是否可以找到Java本机方法的源代码?

请注意,通常应该避免使用Object.clone(),并使用例如复制构造函数,请参阅如何使用Java复制对象?

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