我需要澄清Java中深度复制、浅复制和克隆之间的区别。
不幸的是,“浅拷贝”、“深拷贝”和“克隆”这些术语都没有明确定义。
在Java上下文中,我们首先需要区分“复制值”和“复制对象”。
int a = 1;
int b = a; // copying a value
int[] s = new int[]{42};
int[] t = s; // copying a value (the object reference for the array above)
StringBuffer sb = new StringBuffer("Hi mom");
// copying an object.
StringBuffer sb2 = new StringBuffer(sb);
public class Example {
public int foo;
public int[] bar;
public Example() { };
public Example(int foo, int[] bar) { this.foo = foo; this.bar = bar; };
}
Example eg1 = new Example(1, new int[]{1, 2});
Example eg2 = ...
eg1
将会创建一个新的Example
对象,其foo
等于1,且其bar
字段引用原始数组相同;例如:Example eg2 = new Example(eg1.foo, eg1.bar);
"深拷贝" eg1
的正常解释应该是一个新的 Example
对象,它的 foo
等于 1,并且它的 bar
字段引用了原始数组的一个副本。
Example eg2 = new Example(eg1.foo, Arrays.copy(eg1.bar));
来自C / C ++背景的人可能会说引用赋值产生浅拷贝。然而,在Java上下文中,这不是我们通常所说的浅拷贝的意思...
还存在两个问题/不确定性:
深度有多深?停在两层吗?三级?它是否指整个连接对象图形?
关于封装数据类型;例如字符串?一个字符串实际上不只是一个对象。事实上,它是一个具有一些标量字段和对字符数组的引用的“对象”。但是,字符数组完全被API隐藏。因此,当我们谈论复制字符串时,将其称为“浅”复制或“深”复制是否有意义?还是应该只称其为副本?
最后是克隆。克隆是存在于所有类(和数组)上的方法,通常被认为会产生目标对象的副本。但是:
该方法的规范故意没有说明这是否是浅复制或深复制(假设这是有意义的区别)。
实际上,规范甚至没有明确说明克隆会生成一个新对象。
以下是javadoc的说明:
"创建并返回此对象的副本。 “副本”的确切含义可能取决于对象的类。 一般意图是对于任何对象x,表达式
x.clone()!= x
将为true,并且表达式x.clone().getClass()== x.getClass()
将为true,但这些不是绝对要求。 虽然通常情况下,x.clone().equals(x)
将为真,但这不是绝对要求。"
请注意,这意味着在一个极端情况下,克隆可能是目标对象,而在另一个极端情况下,克隆可能与原始对象不相等。这还假设支持克隆。
简而言之,在OO上下文中,一些人认为Java clone()
方法是有缺陷的,但我认为正确的结论是,在所有对象类型中开发一致且可用的克隆统一模型是不可能的。
x.clone().equals(x)
应该是正确的似乎有些奇怪。我所能想到的对于所有对象类型一致的 equals
意义应该是等同性,而不是任何一种可变类型的实例都被认为是等同的。如果一个对象是不可变的,那么没有克隆它的理由;如果一个对象是可变的,那么它不应该和它的克隆体等同。 - supercat“克隆”一词含义不明确(尽管Java类库包括Cloneable接口),可以指深拷贝或浅拷贝。深/浅拷贝并非专门针对Java,而是涉及到制作对象副本的一般概念,并且指的是如何复制对象的成员。
例如,假设您有一个人类:
class Person {
String name;
List<String> emailAddresses
}
o.clone() == o
可能是真的;请参见我的回答。 - Stephen C