Ste*_*n C 110
不幸的是,"浅拷贝","深拷贝"和"克隆"都是相当不明确的术语.
在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);
Run Code Online (Sandbox Code Playgroud)
简而言之,对类型为引用类型的变量的引用的赋值是"复制值",其中值是对象引用.要复制一个对象,需要new
明确地或在引擎盖下使用某些东西.
现在是对象的"浅"与"深"复制.浅拷贝通常意味着仅复制对象的一个级别,而深度复制通常意味着复制多个级别.问题在于决定一个级别的含义.考虑一下:
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 = ...
Run Code Online (Sandbox Code Playgroud)
正常的解释是,"浅"副本eg1
将是一个新的Example
对象,其foo
等于1,其bar
字段指的是与原始对象相同的数组; 例如
Example eg2 = new Example(eg1.foo, eg1.bar);
Run Code Online (Sandbox Code Playgroud)
"深"副本的正常解释eg1
是一个新的Example
对象,其foo
等于1,其bar
字段指的是原始数组的副本 ; 例如
Example eg2 = new Example(eg1.foo, Arrays.copy(eg1.bar));
Run Code Online (Sandbox Code Playgroud)
(来自C/C++背景的人可能会说引用赋值会产生浅拷贝.但是,这不是我们通常在Java上下文中浅层复制的意思......)
还存在另外两个问题/不确定领域:
深度有多深?它停在两个级别吗?三个级别?它是否意味着连接对象的整个图形?
封装数据类型怎么样; 例如一个字符串?String实际上不只是一个对象.实际上,它是一个带有一些标量字段的"对象",以及对一个字符数组的引用.但是,API完全隐藏了字符数组.因此,当我们谈论复制字符串时,将其称为"浅"副本还是"深度"副本是否有意义?或者我们应该把它称为副本?
最后,克隆.克隆是一种存在于所有类(和数组)上的方法,通常被认为是生成目标对象的副本.然而:
这种方法的规范故意没有说明这是浅层还是深层(假设这是一个有意义的区别).
实际上,规范甚至没有具体说明克隆产生了一个新对象.
这是javadoc所说的:
"创建并返回此对象的副本."copy"的确切含义可能取决于对象的类.一般意图是,对于任何对象x,表达式
x.clone() != x
将为true,表达式x.clone().getClass() == x.getClass()
将为true但这些并不是绝对的要求.虽然通常情况x.clone().equals(x)
确实如此,但这不是绝对的要求."
请注意,这就是说克隆可能是目标对象的一个极端,而在另一个极端,克隆可能与原始对象不同.这假设甚至支持克隆.
简而言之,克隆可能意味着每个Java类都有不同的东西.
有些人认为(正如@supercat在评论中所做的那样)Java clone()
方法已被破坏.但我认为正确的结论是克隆的概念在OO的背景下被打破.在AFAIK中,不可能开发出一种统一的克隆模型,该模型在所有对象类型中都是一致的和可用的.
Ada*_*kin 22
术语"克隆"是不明确的(尽管Java类库包括Cloneable接口),并且可以引用深拷贝或浅拷贝.深/浅副本不是专门与Java相关联的,而是与制作对象副本有关的一般概念,并且指的是如何复制对象的成员.
举个例子,假设你有一个人类:
class Person {
String name;
List<String> emailAddresses
}
Run Code Online (Sandbox Code Playgroud)
你如何克隆这个类的对象?如果要执行浅表副本,则可以复制名称并emailAddresses
在新对象中添加引用.但是如果你修改了emailAddresses
列表的内容,你将修改两个副本中的列表(因为这是对象引用的工作方式).
深层复制意味着您递归复制每个成员,因此您需要为new创建一个新List
的Person
,然后将内容从旧对象复制到新对象.
虽然上面的示例很简单,但深度和浅份副本之间的差异很大,并且对任何应用程序都有重大影响,特别是如果您要提前设计一个通用克隆方法,而不知道某人以后如何使用它.有时候你需要深层或浅层语义,或者某些混合,你可以深层复制某些成员但不需要其他成员.
小智 18