有人能解释深度复制和防御复制之间的区别吗



我的教授说它们完全不同,但我读到它们非常相似。我不知道他指的是什么不同的语境。如果这个问题已经被回答了,就把它链接起来。谢谢。

深度复制:一种对象复制技术:深度复制结构中的所有数据。A";苛刻的";生成对象的深层副本的方法是先序列化,然后反序列化。对于树,递归复制通常是一种有效的解决方案。

防御性拷贝:保护内部数据的操作结果。通常,它是一个深度或浅层副本,用于防止通过内部引用结果进行不希望的访问。例如:

public int[] getValues() {
    return Arrays.copyOf(values, values.length);
}

通常,您应该使用防御副本来保护内部数组、集合或其他结构。如果我们简单地返回(引用)数组,用户可以修改我们的内部结构!

让我们看一个例子。

Tom编写了一个类,该类惰性地缓存Integer集合的一些聚合:

public class CachedIntegerAggregator {
    private List<Integer> integers;
    private boolean isSumCalculated = false;
    private int sum = 0;
    private boolean isMultiCalculated = false;
    private int multi = 0;
    public CachedIntegerAggregator(Integer... integers) {
        this(Arrays.asList(integers));
    }
    public CachedIntegerAggregator(Collection<Integer> integers) {
        this.integers = new ArrayList<Integer>(integers);
    }
    public List<Integer> getIntegers() {
        return integers;
    }
    public int getSum() {
        if (!isSumCalculated) {
            sum = 0;
            for (Integer integer: integers) {
                sum += integer;
            }
            isSumCalculated = true;
        }
        return sum;
    }
    public int getMulti() {
        if (!isMultiCalculated) {
            multi = 1;
            for (Integer integer: integers) {
                multi *= integer;
            }
            isMultiCalculated = true;
        }
        return multi;
    }
}

Jerry以这种方式使用上面的类:

CachedIntegerAggregator aggregator = new CachedIntegerAggregator(2, 3, 4);
// hm, what is the sum?
System.out.println(aggregator.getSum());
// let's print of integers
List<Integer> integers = aggregator.getIntegers();
System.out.println(integers);
// now, see the double
int size = integers.size();
for (int i = 0; i < size; i++) { // (oops! this changes internal data!)
    integers.set(i, integers.get(i) * 2);
}
System.out.println(integers);
// hm, see sum and multi
System.out.println(aggregator.getSum());
System.out.println(aggregator.getMulti()); // (oops! total inconsistency!)

输出:

9
[2, 3, 4]
[4, 6, 8]
9
192

主要问题是什么?汤姆泄露了一个多变的内部结构。解决方案是什么?在getIntegers()中,在返回前复制一份:

public List<Integer> getIntegers() {
    return new ArrayList<Integer>(integers);
}

在某些情况下,一个不可变的包装器可能也是正确的:

public List<Integer> getIntegers() {
    return Collections.unmodifiableList(integers);
}

性能?一般来说,不用担心。快速的对象创建是Java的主要优势之一。当然,也有复杂的结构,它们会被低效地复制。在这种情况下,您可以使用写时复制技术。一些大数据结构实现内置了写时复制支持,例如BigList。

最新更新