【问题标题】:Deep Copy and Shallow Copy Java深拷贝和浅拷贝Java
【发布时间】:2013-11-12 09:51:35
【问题描述】:

我正在为 Java 考试而学习,我发现实际上与理论所教的内容不同。

下面是代码:

    StringBuilder num3[]= new StringBuilder[2];
    num3[0]= new StringBuilder("Pine");
    num3[1]= new StringBuilder("Oak");
    StringBuilder num4[] = new StringBuilder[2];
    System.arraycopy(num3, 0, num4, 0, 2);
    System.out.println(num3[0]==num4[0]);
    System.out.println(num4[0]);
    num3[0] = new StringBuilder("Choc"); 
    System.out.println(num3[0]);
    System.out.println(num4[0]);

输出是:

真的 松树 巧克力 松树

true 表示它是一个浅拷贝,因为 num4[0] 引用了与 num3[0] 相同的对象。但是当我改变 num3[0] 时,我希望 num4[0] 也会改变。

如果是浅拷贝,为什么会发生这种情况?

是因为正在为 num3[0] 创建新对象,而旧的“Pine”StringBuilder 对象正在被 num4 数组引用吗?

如果可以的话,谁能给我一个 System.arraycopy 的例子,这个浅拷贝很明显?

提前致谢, 克里斯约弗

【问题讨论】:

    标签: java arrays shallow-copy


    【解决方案1】:

    System.arraycopy 之后,这两个数组确实是彼此的浅拷贝。也就是说,您有一份到StringBuilders 的references 副本。所指的对象都是一样的。

    num3:  [ refA, refB ]                        num4: [ refA, refB ]
               |    |                                      |     |
               |    `-------> StringBuilder("Oak") <-------+-----' 
               `------------> StringBuilder("Pine") <------'
    

    但随后您将num3[0] 中的reference 更改为指向新的StringBuilder

    num3:  [ refC, refB ]                        num4: [ refA, refB ]
               |    |                                      |     |
               |    `-------> StringBuilder("Oak") <-------+-----' 
               |              StringBuilder("Pine") <------'
               `------------> StringBuilder("Choc")
    

    底层对象没有改变。您所做的只是更改了引用它们的内容。


    System.arraycopy 就像使用 for 循环复制数组中的值一样,除了它对于大型数组更有效(因为现代 CPU 具有用于批量内存复制的特殊指令)。你永远不需要它语义;这只是一个性能优化。

    【讨论】:

    • 引用与指针非常非常相似,只是你不能对它们进行算术运算,并且它们会在内存管理系统中保持它们所引用的内容。
    【解决方案2】:

    您没有更改 num3[0],而是将其指向其他东西,即一个新的 StringBuilder 对象。 num3[0] 和 num4[0] 不再指向同一个对象,因此 num4[0] 不会改变。数组引用的值被复制,但如果您修改这些值,它不会影响另一个数组。如果您修改它们指向的对象,而不是对这些对象的引用,则会对其产生影响。

    num3[0]num4[0] 想象成两个人指着一个背包。 如果您在背包中添加东西,它们都会指向已更改的内容。但是,如果您让num3[0] 指向电视,num4[0] 仍然会指向背包。

    【讨论】:

      【解决方案3】:

      是不是因为新对象是为 num3[0] 和旧的 “Pine”StringBuilder 对象被 num4 数组引用?

      没错。

      如果可以的话,谁能给我一个 System.arraycopy 的例子,这个浅拷贝很明显?

      我不确定我是否理解这个问题。没有任何副本,无论是浅的还是深的,都不会影响您副本之后所做的事情。

      但请尝试以下操作:

      num3[1].append("tree");
      println(num4[1]);        // should be "Oaktree"
      

      【讨论】:

      • 感谢您的回答。我尝试追加并看到它们显然指向内存中的同一个对象。但这使事情变得更加复杂,因为 Arrays.copyOf 应该是一个深层副本,但现在它似乎正在将树附加到 num4 中。任何想法为什么会发生这种情况?
      • 不,它不是深拷贝。谁说的?
      • Richard M Reese 撰写的 Oracle Certified Associate Java SE 7 Programmer 学习指南。
      • @HereToLearn 您首先应该信任 API 文档,它说:For all indices that are valid in both the original array and the copy, the two arrays will contain identical values 在深层副本中,重点是对象身份丢失(通过克隆对象和子对象) )。仅凭这一点就可以断定它不是深拷贝:因为,JVM 怎么知道如何深拷贝任意对象?请注意,元素类型限制为可克隆或可序列化。
      【解决方案4】:

      num3[0]num4[0] 是对同一个对象的引用,因此当您更改 num3[0] 时,它只会更改 num3[0] 指向的位置,而不会更改 num4[0] 指向的位置。

      System.out.println(num3[0]==num4[0]);
      

      这会返回 true,因为两者都指向同一个对象,但都是独立的。

      如果你会做类似的事情

      num3[0].append("something") 然后你会打印 num3[0]num4[0] 那么两者都会受到影响。

      【讨论】:

        【解决方案5】:

        在这种情况下,浅拷贝只不过是对对象的新引用。 你有一个对象Pine 和一个指向它的指针,它存储在int num3 中。现在您制作一个存储在num4 中的浅拷贝(创建一个新引用)。您现在拥有一个带有两个引用的 Pine 对象。当您将Choc 分配给num3 时,引用数减少1,并且您有效地将num3 持有的引用替换为另一个引用。 Pine 从未以任何方式更改。

        【讨论】:

          猜你喜欢
          • 2012-04-12
          • 2015-01-13
          • 1970-01-01
          • 2012-04-13
          • 1970-01-01
          • 1970-01-01
          • 2011-09-05
          • 1970-01-01
          • 2011-03-24
          相关资源
          最近更新 更多