61

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

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

在 Java 中,什么是浅拷贝?

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

4

11 回答 11

91

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

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
于 2009-07-24T03:50:18.027 回答
17

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

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

于 2009-07-24T03:38:30.593 回答
9

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

Object B = A;

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

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

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

于 2009-07-24T03:43:14.437 回答
3

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

于 2009-07-24T03:38:01.060 回答
2

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

于 2009-07-24T05:10:29.533 回答
2

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

于 2019-10-13T02:19:10.183 回答
1

你从哪里得到这个文件?

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() 中返回一个浅拷贝。

于 2009-07-24T03:38:04.960 回答
1

首先,如果我们谈论一维数组,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有时很难理解,所以尝试一下...

玩得开心!

于 2012-10-20T07:38:39.687 回答
1

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

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

于 2017-04-05T14:02:22.783 回答
0

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

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

于 2019-07-16T04:23:33.997 回答
0

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

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

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

于 2017-11-09T09:42:30.773 回答