为什么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
,而不是Object
。 B
不知道A
中所有内容的实现,因此需要调用A
的clone
方法。 但是如果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 ++编写的,以提高性能。