在 Java 中,啥是浅拷贝?

Posted

技术标签:

【中文标题】在 Java 中,啥是浅拷贝?【英文标题】:In Java, what is a shallow copy?在 Java 中,什么是浅拷贝? 【发布时间】:2010-11-13 14:54:30 【问题描述】:

java.util.Calendar.clone() 返回“...具有相同属性的新日历”并返回“此日历的浅表副本”。

这似乎不是在 SO 上回答 here 的浅拷贝。该问题被标记为 language-agnostic, Java 似乎不遵循语言不可知论的定义。当我单步执行代码时,我注意到结构和元素被复制到这个新对象中,而不仅仅是与语言无关的结构。

在 Java 中,什么是浅拷贝?

它与 Java 深拷贝(如果存在的话)有何不同?

【问题讨论】:

好的。看起来外卖是 Java 浅/深副本与其他编程世界相同,只是 android 1.5 java.util.Calendar.clone() 文档是错误的。 clone() 是深拷贝,不是浅拷贝。 【参考方案1】:

浅拷贝只是复制类中引用的值。深拷贝复制这些值。给定:

class Foo 
  private Bar myBar;
  ...
  public Foo shallowCopy() 
    Foo newFoo = new Foo();
    newFoo.myBar = myBar;
    return newFoo;
  

  public Foo deepCopy() 
    Foo newFoo = new Foo();
    newFoo.myBar = myBar.clone(); //or new Bar(myBar) or myBar.deepCopy or ...
    return newFoo;
  


Foo myFoo = new Foo();  
Foo sFoo = myFoo.shallowCopy();  
Foo dFoo = myFoo.deepCopy();  

myFoo.myBar == sFoo.myBar => true  
myFoo.myBar.equals(sFoo.myBar) => true  
myFoo.myBar == dFoo.myBar => **false**  
myFoo.myBar.equals(dFoo.myBar) => true  

在这种情况下,浅拷贝具有相同的引用 (==),而深拷贝只有等效的引用 (.equals())。

如果对浅拷贝引用的值进行了更改,则副本会反映该更改,因为它共享相同的引用。如果对深度复制的引用的值进行了更改,则该副本不会反映该更改,因为它不共享相同的引用。

中国主义

int a = 10; //init
int& b = a; //shallow - copies REFERENCE
int c = a;  //deep - copies VALUE
++a;

结果:

a is 11  
*b is 11  
c is 10

【讨论】:

如果更改浅拷贝会发生什么?它会改变浅拷贝的引用吗? 视情况而定。 newFoo.bar=newBar 只会改变newFoobar,而不是foo。但是newFoo.bar.avalue=1 会改变两者。 这里的酒吧是什么?在“私人酒吧 myBar”中 只是说:equals()是Object类的一个方法。 == 应在参考比较期间使用。 == 检查两个引用是否指向相同的位置。应该使用 equals() 方法进行内容比较。【参考方案2】:

浅拷贝只是一组指向相同内存位置的指针。实际上它不会创建真正的副本,因此内存使用率较低。

在深拷贝的情况下,会创建内存段的精确副本,并将指针设置为新的内存位置。所以理论上这种情况下内存消耗应该是两倍。

【讨论】:

这是一个 Java 问题,而不是 C/C++ 问题,因此谈论内存段具有误导性。 别想那么多高水平。使用任何语言处理物理硬件,如内存和 CPU。即使我们不使用alloc,mallocs也不认为我们没有在Java中使用内存。 放松,斯蒂芬...对于来自 C/C++ 背景的人来说,Chathuranga 的回答实际上很有帮助。对同一个问题有不同的看法是件好事。【参考方案3】:

浅拷贝是指向对象的引用指针的拷贝,而深拷贝是对象本身的拷贝。在 Java 中,对象被保存在后台,在处理对象时通常与指针交互。变量名指向对象的内存空间。当您将一个变量设置为等于另一个变量时,会进行浅拷贝,如下所示:

Object B = A;

可以通过获取对象 A 的属性并将它们放入新对象 B 中来进行深度复制。

Object B = new Object(A.getProperty1(), A.getProperty2()...);

这会影响程序行为,因为如果您制作浅拷贝并在其上执行任务,则会影响对象的所有浅拷贝。如果您对深层副本进行更改,则只有该副本受到影响。我希望这对你来说已经足够详细了。

【讨论】:

不对。如果你在做 Object B = A ,你就是在给 A 起别名。这不是浅拷贝,而这只是一个赋值。如果您执行 A.clone() 并将其分配给 B ,那将是浅拷贝的正确定义。【参考方案4】:

1.6 文档文档 Calendar.clone 为“创建并返回此对象的副本”。 Object.clone 指定的文字浅拷贝没有任何意义。 Java 在相当典型的意义上使用术语“浅拷贝”。

【讨论】:

【参考方案5】:

这似乎是文档中的错误。我看不出 Android 的 Calendar.clone 方法如何满足“浅拷贝”的典型定义(用 Java 或其他方式)。

【讨论】:

【参考方案6】:

浅拷贝是通过引用传递... 深度复制是通过值传递…… 上下文不同,但过程完全相同。首先要记住,在 Java 方法调用中,原语是按值传递的,而对象是按引用传递的(在其他语言中,对象也可以按值传递)。现在,在 Java 中,当调用者将原语传递给被调用方法时,被调用方法只是将其克隆为新的局部变量,这是一个深拷贝。而如果传递了一个对象,则被调用的方法只会对同一对象进行新的本地引用,这是浅拷贝。如果你理解调用,你就会理解深/浅拷贝,反之亦然。

【讨论】:

你能再解释一下吗?按值传递或按引用传递通常用于方法调用的上下文,而不是克隆。 你更应该edit你的问题而不是添加cmets。并非所有的 cmets 最初都可以看到。在答案中将所有内容放在一个位置。【参考方案7】:

您从哪里获得此文档?

java.sun.com 上的官方 Java 6 文档只是让 Calendar.clone() 返回对象的副本。不提浅。

更一般地说,Java 中的浅拷贝是指您获得一个新对象引用但新对象持有(直接或间接)对原始数据的引用。

例如:

class MyClass
  private List<Integer> innerList;

  public MyClass(List<Integer> list)  innerList = list; 

  //Some code...

  public Object clone()
    return new MyClass(innerList);
  

在其 clone() 中返回一个浅拷贝。

【讨论】:

文档来自最新的Android 1.5 SDK 注意clone()不应该根据Object.clone()规范调用构造函数。 是的,是的,从技术上讲,您应该从 super.clone() 中获取一个对象;将其转换为 MyClass 然后神奇地设置字段。不过要显示的代码要多得多,问题的症结在于浅/深的区别。【参考方案8】:

首先,如果我们谈论一维数组,ArrayList 的Javadoc 是有些错误的,因为它使用了Arrays 中的copyOf 方法。所以 clone() 会返回一个一维副本,至少从 1.5 开始(我没有进一步测试)!这就是Java中“浅”的意思:一维

您可以在此处阅读更多信息:http://www.javapractices.com/topic/TopicAction.do?Id=3。所以 clone() 不是浅拷贝!如果你想要一个真正的一维数组的浅拷贝,只需引用它:

Array a = new Array();
Array b = a;                    //a is only a shallow copy, nice for synchronisation

Java 中的数组很棘手,这也是因为 Java 确实是按值传递的,但数组的值只是它们的指针!另一方面,这允许我们同步对象,这是一件好事。尽管如此,如果您在数组(或 ArrayLists)中使用数组,仍然存在一些问题,因为容器数组(或 ArrayList)的 clone() 不会复制它们的值,只会复制它们的引用!所以你根本不应该将任何数组放入数组中,你应该只处理数组中的对象!

而且 Javadoc 有时很难理解,所以请尝试测试一下...

玩得开心!

【讨论】:

【参考方案9】:

浅拷贝只是将对象引用复制到目标引用中。它不会在堆上创建新对象。 默认情况下,Java 使用 clone() 函数进行浅层克隆。

要在堆上获取新对象,必须执行深度克隆,可以通过序列化和反序列化来实现。

【讨论】:

@Akansha :您能否分享一下您提到的上述理论的代码。因此很容易理解如何使用序列化和反序列化执行深度克隆。还有浅拷贝【参考方案10】:

在浅拷贝中,克隆对象具有原始值的副本,但对象引用引用与原始副本相同的对象。 浅拷贝有一个显着的缺点,克隆对象和原始拷贝指的是同一个地址对象。克隆对象在地址对象中所做的任何更改也将反映在原始副本中,这是一种不需要的行为。我们真正想要的是用户对象的两个独立副本。对于这种情况,深度复制可以帮助我们解决问题。

深度复制不仅会复制原始值,还会创建对象引用的副本。

您可以在这里查看工作示例:https://codingninjaonline.com/2017/11/09/deep-vs-shallow-copy/

【讨论】:

【参考方案11】:

浅拷贝:在此克隆中,对克隆对象的任何更改也会反映到原始对象。

深拷贝:在此克隆中,分配了单独的克隆内存,这意味着对克隆对象的任何更改都不会反映到原始对象。

【讨论】:

以上是关于在 Java 中,啥是浅拷贝?的主要内容,如果未能解决你的问题,请参考以下文章

java 中对象赋值 是浅拷贝还是深层拷贝

Java - clone 方法属于浅拷贝 OR 深拷贝?

Java - clone 方法属于浅拷贝 OR 深拷贝?

list.addAll()是浅拷贝,如何实现list的深拷贝

list.addAll()是浅拷贝,如何实现list的深拷贝

Java 浅拷贝和深拷贝