何时使用对象引用的深度克隆?或多久一次

本文关键字:一次 对象引用 深度 何时使 | 更新日期: 2023-09-27 18:25:58

我知道什么是deep copyshallow copyhow to deep copy等,但我主要的疑问是何时深度复制对象引用?或者多久一次


场景1:
考虑一个代码,有关完整代码,请参阅http://pastebin.com/WEgeBFNb

class Box{
        Position pos;
        Box(Position p){ 
           pos = p;      
        }
        Position getPosition(){
          return pos;
        }
   }

和类似main()

public class Sample{
      public static void main(String args[]){
        Position pos = new Position(3,5);
        Box box = new Box(pos);     
        pos.setX(5);
        System.out.println( box.getPosition().getX()); 
            // Will print 5, but I want Box to retain its value
      }

我通过以下方式达到了上述要求:

 Box(Position p){ 
           pos = new Position(p);     // Deep cloning 
        }

然后我必须在Position中也有一个复制构造函数,比如:

Position(Position p){
     x  = p.x;
     y  = p.y;
   }

但我的问题是:何时使用深度克隆

场景2:例如,考虑一个c#代码。

List<Accounts> = Mysession.getAllAccounts();。在这里,我希望返回对象中的更改不能反映在会话对象中。(这种情况不仅出现在C#中,而且通常出现在任何oop语言中)所以,如果我开始深度克隆,那么这不是一项容易的任务,因为它可以深入到5个级别的对象,并且有一个关系

再一次,我知道要获得准确的100%,我必须深度克隆。同意

  1. 什么更常见?是否返回引用或对象的副本
  2. 我听说,深度克隆是一个繁琐的过程,必须避免。那么深度克隆的频率是多少
  3. 您能给出一些示例场景(不需要代码)吗
  4. 当初始化像上面的box示例一样时,必须使用克隆pos = new Position(p)?还是正常分配CCD_ 10

何时使用对象引用的深度克隆?或多久一次

面向对象编程的主要目的必须是确保对象在任何时候都处于合法状态。

因此,当您返回对象的引用时,您应该考虑:

  1. 返回的对象是不可变的吗
  2. 返回引用(主对象)的当前对象是否具有依赖于返回对象的值?(派生值或缓存值)

您可以通过以下方式对这些问题的答案作出反应:

返回的引用是一个不可变的对象(String、BigDecimal等)

  1. 无需任何操作

返回的引用是一个多表对象(数组、日期等),但主对象没有派生值(例如仅装饰它)

  1. 无需任何操作

返回的引用是一个多表对象(数组、日期等),主对象具有派生值

  1. 在返回对象之前先对其进行复制。如果复制很容易,并且不占用内存或时间(取决于您的非功能要求),则此方法适用。

  2. 返回对原始对象的不可修改的引用(如Collections.unmodified…do)。

  3. 返回一个代理,该代理检测对返回对象的访问,并将这些更改通知主对象,以便主对象可以重新计算派生值,并且不会处于不一致的状态

当你得到一个对象引用时,问自己同样的问题。通过构造函数或方法调用。

与其考虑"深层"或"浅层"克隆,不如考虑每个封装的对象引用所代表的内容。假设某个类实例George的字段Foo包含类型为IList<String>的引用。这样的字段可以代表至少五种不同的东西:

  • 对不可变类型实例的引用,用于封装其中包含的字符串。

  • 对对象实例的引用,该对象实例的类型可能是可变的,但永远不会暴露于任何可能使其发生变异的东西,用于封装其中包含的字符串。

  • 唯一存在于世界任何地方的引用,在George方法的调用堆栈之外,指向一个可变列表,George正在使用该列表来封装其状态。

  • 对其内容可能会更改的列表的引用,该列表构成其他对象可变状态的一部分。该字段不用于封装列表的内容,而是其标识

  • 对列表的引用,该列表的内容可能会更改,其内容被认为是乔治州的一部分,并且存在外部持久引用。

如果Foo是前两种类型,则George的适当副本可以使其Foo指代与George.Foo相同的列表、将始终保持相同内容的新构建的列表或将始终保持同样内容的任何其他列表。如果是第三种类型,George的一个适当副本必须使其Foo引用一个新列表,该列表中预加载了George.Foo中项目的副本。如果是第四种类型,则正确的副本必须使其Foo引用与George.Foo相同的对象,并且不引用副本。如果是第五种类型,则不能孤立地克隆George

如果列表项是可变类型(而不是String),则必须确定应用于列表中包含的项的五个目的中的哪一个,并将每个列表项视为一个字段。请注意,对于逻辑上不可变的类型,其中包含的任何引用都必须是可共享的。如果一个对象的正确行为要求它持有引用的某个对象不是任何其他引用的目标,那么这意味着持有引用的对象应该只存在一个引用。