I need clarification on the differences between deep copy, shallow copy, and clone in 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);
简而言之,对类型为引用类型的变量的引用分配是“复制值”,其中该值是对象引用。要复制对象,需要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 = ...
正常的解释是的“浅”副本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所说的:
“创建并返回此对象的副本。“ copy”的确切含义可能取决于对象的类。通常的意图是,对于任何对象x,表达式x.clone() != x将为true,而表达式x.clone().getClass() == x.getClass()将为true ,但这不是绝对要求。虽然通常情况x.clone().equals(x)是正确的,但这不是绝对要求。”
请注意,这就是说克隆可能是目标对象,而另一极端是克隆可能与原始对象不相等。并假设甚至支持克隆。
简而言之,对于每个Java类,克隆都可能意味着不同的东西。
有人认为(如@supercat在注释中所做的那样)Java clone()方法已损坏。但是我认为正确的结论是,克隆的概念在面向对象环境中被打破了。在AFAIK中,不可能开发出一个统一的克隆模型,该模型在所有对象类型上都是一致且可用的。
问题内容: 是Java中的浅拷贝? 最终,这到达Object(最上面的类)的clone()方法,该方法创建与该对象相同类的新实例,并将所有字段复制到该新实例(“浅表副本”)。 我从维基百科阅读了此内容。 我不明白为什么它是浅表。将创建一个包含所有字段的新实例。这只是深复制吗?困惑。需要给我一些解释。 问题答案: 默认确实是浅表副本。但是,除非您的对象实现,否则它的设计宗旨是抛出一个。 并且在实现时
问题内容: 题: 在这里,“ MyClass”类可以通过调用“ Object”类中的clone方法来克隆其自己的对象。当我尝试在同一包“ GoodQuestions”中的另一个类(“ TestSingleTon”)中克隆此类(“ MyClass”)的类时,会引发以下编译时错误。 “来自对象类型的方法clone()不可见 ” 所以这是引发上述错误的代码? 问题答案: 发生此错误的原因是在对象类中cl
问题内容: 我需要Java的浅表副本,是否应该使用或遍历原始列表并将元素复制到新的arrayList中,这会更快? 问题答案: 使用或使用复制构造函数。 复制构造函数从传递的集合到数组进行其他转换,而该方法直接使用内部数组。 请记住,回报,所以您将不得不放弃。
问题内容: Java固有地被破坏了。具体来说,我与接口有关的最大问题是,它期望方法行为无法定义方法本身。因此,如果遍历列表,则必须使用反射来访问其定义的行为。但是,在Java 8中,我们现在有了默认方法,现在我问为什么在中没有默认方法。 我知道为什么接口不能使用默认的Object方法,但是,这是一个明确的设计决定,因此可以进行例外处理。 我有点设想过时,并将其内部代码更改为以下内容: 并且继续进行
问题内容: 我已经为Employee类的父类是抽象的并且父类中的clone()方法是抽象的编写了此克隆方法。我想用此代码复制Employee对象的原始数据类型,而不是复制每个原始数据单独键入,但是此代码在我调用clone()方法的行中有问题。(此代码在Employee类中) 错误是:来自对象类型的方法clone()不可见。 但是我的Employee类在类层次结构中,可以访问Object类中受保护的
问题内容: 我一直想在Google上找到这四个之间的区别,我希望这方面会有大量的信息,但是这四个调用之间确实没有任何可靠的比较。 我着手尝试汇编一下这些系统调用之间的区别的基本概况,这就是我得到的。所有这些信息是否正确/我是否缺少任何重要信息? :fork调用基本上是对当前过程进行复制,几乎在所有方面都相同(例如,并非在某些实现中都复制了所有内容,例如,在某些实现中资源有限,但其想法是创建尽可能近
问题内容: 我无法克隆HTTPS存储库。我可以克隆SSH仓库,但不能克隆HTTPS仓库。由于位于公司防火墙后面,因此无法测试GIT协议。 这就是我想要做的: 到目前为止,我已经尝试了以下方法(基于Google搜索) 通过清除和安装Git 通过安装Git 安装curl开发库 安装Expat库 下载Git源码并使用以下命令构建: 还尝试将configure指向curl二进制文件() 我已经尝试了所有可
问题内容: 众所周知,Java 中的接口已损坏。造成这种情况的原因很多,我将不再赘述。其他人已经做到了。这也是Java架构师本身的立场。 因此,我的问题是:为什么还不被弃用?如果核心Java团队已确定它已损坏,那么他们还必须考虑过时。他们反对这样做的原因是什么(在Java 8中仍不建议弃用)? 问题答案: 有一个错误在1997年提交给Java的错误数据库有关添加方法,所以将不再是无用的。它以“无法