在Java中,什么是浅拷贝?

时间:2009-07-24 03:32:44

标签: java clone shallow-copy

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

在SO上回答here时,这似乎不是浅层副本。该问题被标记为语言无关, Java似乎不遵循语言无关的定义。当我逐步完成代码时,我注意到结构和元素被复制到这个新对象,而不仅仅是语言无关的结构。

在Java中,什么是浅层副本?

它与Java深层副本(如果存在)有何不同?

11 个答案:

答案 0 :(得分:71)

浅拷贝只是复制类中引用的值。深拷贝会复制值。给出:

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())。

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

C-ISM

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

答案 1 :(得分:11)

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

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

答案 2 :(得分:5)

浅表副本是指向对象的引用指针的副本,而深层副本是对象本身的副本。在Java中,对象保留在后台,通常在处理对象时与之交互的是指针。变量名称指向对象的内存空间。当您将一个变量设置为另一个变量时,会生成浅表副本,如下所示:

Object B = A;

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

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

这似乎是文档中的错误。我没有看到Android的Calendar.clone方法有什么作用符合“浅拷贝”的典型定义(用Java或其他方式)。

答案 5 :(得分: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()中返回一个浅表副本。

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

首先,如果我们讨论的是一维数组,那么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有时很难理解,所以试试看......

玩得开心!

答案 8 :(得分:0)

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

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

您可以在此处查看有关此问题的工作示例:https://codingninjaonline.com/2017/11/09/deep-vs-shallow-copy/

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

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