为什么Object.clone()是Java原生的?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么Object.clone()是Java原生的?相关的知识,希望对你有一定的参考价值。
创建对象的精确副本的clone
上的clone
方法声明为:
Object
为什么是protected native Object clone() throws CloneNotSupportedException;
?
基本上,因为native
方法所做的事情是您无法用Java语言完成的:它会克隆对象的状态,包括其实际的类名称。
Java中的克隆机制基于调用超类的clone()
方法的每个类,一直到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
。 Object
不知道B
中所有内容的实现,因此需要调用A
的A
方法。但是,如果clone
是用Java语言实现的A
而不是调用clone
,则它将返回的对象必须是super.clone()
。它不能使用A
(假设创建A时不知道B)。
它可以通过反射来做些什么,但是它怎么知道要调用哪个构造函数,以便所有最终字段都可以正确填充?
所以诀窍是new B()
本身并没有执行,而是调用了A
,并一直返回到super.clone()
,并且它使用了一个本机方法来逐字节复制原始对象,调整为新的堆位置。因此,新对象神奇地成为Object
对象,并且类型转换不会失败。
为什么不返回B
?因为那不会被克隆。当您调用Object
时,您希望得到的对象具有相同的状态(字段)和相同的class(重写和添加的方法)。如果它返回一个内部类名称为clone
的对象,则您只能访问Object
提供的内容,例如Object
,并且您将无法从另一个toString()
访问其私有字段。对象,或将其分配给B
类型变量。
查看克隆文档:
否则,此方法将创建该类的新实例。对象,并使用确切的内容初始化其所有字段该对象的相应字段,就像通过分配;内容本身不是克隆的字段。
此操作可以使用本机代码非常有效地完成,因为必须直接复制一些内存。在这方面与B
类似,它也是本机的。有关详细信息,请参见以下问题:System.arrayсopy
注意,通常应避免使用Object.clone(),而应使用例如复制构造函数,请参见Is it possible to find the source for a Java native method?
clone()是本机的,因为操作取决于基础平台,也称为OS。以下是一些事实,可以帮助您了解实际发生的情况:1. JVM是用C ++实现的2. C ++要求您在目标平台/ OS上编译代码3. clone()操作在内存中进行4.内存的那部分是由JVM(一个C ++程序)控制的5.将一个类编译为bytecode = text(忽略下面所有冗长的细节,它们仅用于说明)因此,此:
How do I copy an object in Java?
成为此:
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));
}
- 共享类方法=>不克隆;它们在内部用指针标识(一种方法不过是一组指令,因此,指向该组只需要指向该组的第一条指令)
- 然后,对象只是实例变量的特定状态
- 要克隆对象,方法clone()仅需要在内存中复制实例变量。看起来它是以字节块的形式进行的-与调用构造函数new SomeClassname()一样,没有进行任何分析-找出类在内存中的位置,构造函数在哪里,然后找出变量类型,并通过初始化值(如果有),并执行分配(如果有)。您得到图片。
- 由于clone()是在给定类型的特定对象上调用的,因此该类型本身是已知的,因此不需要上面(8)中的分析。即使实例变量的值相同,所以我们不需要传递任何东西。
- 剩下的唯一问题是DEEP副本-通过在每个依赖项对象上调用clone()并将引用分配给包装克隆中的实例变量来处理对依赖项的引用。
[我知道这很长,但是很清楚地说明了为什么clone()比“ new”更快-只是将对象的状态捕获为二进制字符串并复制它而没有任何“ thought” /检查。
要了解有关字节码的更多信息,请查看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
。并记住.... 计算机科学中的一切都是伪造的,包括类对象,因为它们只是将相关结构和功能组合在一起的另一个抽象],可能是硬件:),但大多数人认为它实际上属于计算机工程,而不是CS。
这是本机的,因为某些系统类的this article in DZone方法是用C ++编写的,以提高性能。
以上是关于为什么Object.clone()是Java原生的?的主要内容,如果未能解决你的问题,请参考以下文章