`
liumingwei2009
  • 浏览: 9522 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

java 深克隆与浅克隆

 
阅读更多
大家知道,对象是互相引用的,即对象中可能包含了另一个对象的引用,举例如:有一个Order对象,Order对象中又包含了LineItems对象,然后LineItems对象又包含了Item对象。


好了,现在我有一个Order对象order1,它包含了一个LineItems对象items,这表示的是有一个订单order1,订单的内容是items。


好的,现在有另一个客户想要一份订单,内容跟order1完全一样,那么在系统的逻辑层我们怎么做呢?很简单,order2=order1.clone(). 我们知道clone方法是在内存中生成一个新的对象,而不是只得到原对象的引用。这时候,有人说话了:“哦,明白了我们对order2的成员变量进行修改,是不会影响order1的。” 很可惜,这句话只对了一半。


假设order类有一个成员变量name,当然改变order2.name不会影响order1.name,因为他们在不同的内存区域。但是如果改变 order1.items呢?很遗憾,简单地使用order1.clone,是会影响到order2.items的。原因很简单,就是因为clone方法默认的是浅克隆,即不会克隆对象引用的对象,而只是简单地复制这个引用。所以在上例中,items对象在内存中只有一个,order1和order2都指向它,任何一个对象对它的修改都会影响另一个对象。


那相对浅克隆,深克隆自然就是会克隆对象引用的对象了。也就是说,在上例中,改变order1.items并不会影响order2.items了。因为内存中有两个一样的items。


如果实现深克隆?

一个方法自然是重写clone方法,添加如order.items=(LineItems)items.clone()的语句,也就是人为地添加对引用对象的复制。这个方法的缺点是如果引用对象有很多,或者说引用套引用很多重,那么太麻烦了。业界常用的方法是使用串行化然后反串行化的方法来实现深克隆。由于串行化后,对象写到流中,所有引用的对象都包含进来了,所以反串行化后,对等于生成了一个完全克隆的对象。绝!


这个方法的要求是对象(包括被引用对象)必须事先了Serializable接口,否则就要用transient关键字将其排除在复制过程中。

克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float 等,也同时含有非基本数据类型如(数组,集合等)
被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).

但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)

以下举个例子,说明以上情况:

被克隆类:ShadowClone.java

class ShallowClone implements Cloneable {
    public int a;
    public int[] b;
    
    public ShallowClone() {
        a = 5;
        b = new int[] {1, 2, 3, 4, 5};
    }
    
    // 浅克隆,对于克隆后的对象,只能保证对基础类型成员的修改不会影响原对象的相应成员
    // 对类类型和数组类型的成员,只是拷贝了对象的地址,因此对克隆后对象的这些类型成员
    // 进行修改会影响原对象
    @Override
    public Object clone() {
        ShallowClone sc = null;
        try {
            sc = (ShallowClone)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return sc;
    }
}

测试类:

public class DeepAndShallowClone {
    public static void main(String[] args) throws Exception {
        // shallow clone
        ShallowClone sc = new ShallowClone();
        ShallowClone scCopy = (ShallowClone)sc.clone();
        
        
        System.out.println("Shallow Copy");
        System.out.println("--Before clone");
        System.out.println("sc.a=" + sc.a);
        System.out.println("sc.b=" + sc.b[0]);
        
        scCopy.a = 1;
        scCopy.b[0] = 10;
        System.out.println("--After clone");
        System.out.println("sc.a=" + sc.a);
        System.out.println("sc.b=" + sc.b[0]);
        System.out.println("scCopy.a=" + scCopy.a);
        System.out.println("scCopy.b=" + scCopy.b[0]);
    }
}

结果如下:

ShallowCopy
--Beforeclone
sc.a
=5
sc.b
=1
--Afterclone
sc.a
=5
sc.b
=10
scCopy.a
=1
scCopy.b
=10

问题出现了,修改了克隆后的对象scCopy.b[0]的值,但sc.b[0]的值也改变了,与scCopy.b[0]的值相等.
以下针对浅克隆得出结论:基本类型是可以被克隆的,但引用类型只是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,修改其中一个,当然另一个也就变了.
由此可见,浅克隆只适合克隆基本类型,对于引用类型就不能实现克隆了.

那如何实现克隆引用对象呢,以下提供一种方法. 用序列化与反序列化实现深克隆(deep copy)

被克隆对象.DeepClone.java

class DeepClone implements Serializable {
    private static final long serialVersionUID = 1L;
    public int a;
    public int[] b;
    
    public DeepClone() {
        a = 10;
        b = new int[] {6, 7, 8, 9, 10};
    }
    
    // 使用ObjectInput(Output)Stream和ByteArrayInput(Output)Stream实现深克隆
    public Object deepClone() throws IOException, ClassNotFoundException {
        DeepClone dc = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);
        oos.close();
        
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream bis = new ObjectInputStream(bais);
        dc = (DeepClone)bis.readObject();
        return dc;
    }
}

测试类:

public class DeepAndShallowClone {
    public static void main(String[] args) throws Exception {
        DeepClone dc = new DeepClone();
        DeepClone dcCopy = (DeepClone)dc.deepClone();
        System.out.println("--Before clone");
        System.out.println("dc.a=" + dc.a);
        System.out.println("dc.b=" + dc.b[0]);
        dcCopy.a = 1;
        dcCopy.b[0] = 1;
        
        System.out.println("Shallow Copy");
        System.out.println("--After clone");
        System.out.println("dc.a=" + dc.a);
        System.out.println("dc.b=" + dc.b[0]);
        System.out.println("dcCopy.a=" + dcCopy.a);
        System.out.println("dcCopy.b=" + dcCopy.b[0]);
    }
}

结果如下:
--Beforeclone
dc.a
=10
dc.b
=6
ShallowCopy
--Afterclone
dc.a
=10
dc.b
=6
dcCopy.a
=1
dcCopy.b
=1

writeObject方法会将被克隆类的对象关系网都写出,这样就可以实现深克隆。当然,每个被克隆的成员类型必须实现Serializable接口


分享到:
评论

相关推荐

    java 深克隆 浅克隆

    在Java编程语言中,克隆是...理解深克隆和浅克隆的概念对于编写复杂的、性能敏感的Java程序至关重要,尤其是在处理大量对象或需要保持数据独立性时。根据具体需求选择合适的克隆策略,可以提高代码的灵活性和可维护性。

    java 深克隆浅克隆

    Java提供了两种主要的克隆方式:深克隆和浅克隆。 1. 浅克隆(Shallow Clone): 浅克隆是通过调用对象的`clone()`方法来实现的,这个方法是Object类提供的。当一个对象被浅克隆时,新创建的对象将拥有原始对象的...

    JAVA浅克隆与深克隆

    测试类`Test.java`可以用来验证浅克隆和深克隆的效果,比如改变克隆对象的属性并检查原始对象是否受影响。 总结来说,Java中的克隆机制对于复制对象和维护对象状态的独立性至关重要。浅克隆适用于不涉及复杂引用...

    Java对象的深克隆与浅克隆详解.zip(wcb2003)

    Java提供了两种主要的克隆方式:浅克隆(Shallow Clone)和深克隆(Deep Clone)。下面我们将深入探讨这两种克隆方法。 ### 浅克隆 浅克隆是指创建一个新的对象,其成员变量的值与原对象相同,但引用类型成员变量...

    java的深度克隆的例子

    首先,Java提供了两种基本的克隆方式:浅克隆(shallow clone)和深克隆。浅克隆仅仅复制对象本身,而不复制对象所引用的对象。而深克隆则会递归地复制对象及所有引用的对象,确保新创建的对象和原始对象及其引用的...

    java 对象的克隆(浅克隆和深克隆)

    在Java中,有两种主要的克隆方式:浅克隆(Shallow Clone)和深克隆(Deep Clone)。接下来,我们将详细讨论这两种克隆方式及其区别。 1. 对象的浅克隆: 浅克隆是指创建一个新对象,这个新对象复制了原对象的所有...

    实例分析java对象中浅克隆和深克隆

    浅克隆和深克隆在Java对象中的应用 在Java编程语言中,克隆(Clone)是一种常见的对象复制机制。克隆可以分为浅克隆(Shallow Clone)和深克隆(Deep Clone)两种。浅克隆仅复制对象的引用,而深克隆则完全复制对象...

    克隆和序列化(Java )

    在Java中,克隆分为浅克隆和深克隆。浅克隆只是复制对象的引用,而深克隆则会创建对象以及所有嵌套对象的新副本。 2. **浅克隆**:通过实现`Cloneable`接口并重写`Object`类的`clone()`方法,可以实现浅克隆。浅...

    Java浅克隆与深克隆

    Java中的浅克隆和深克隆是对象复制的两种方式,它们主要区别在于对对象内部引用类型字段的处理。在Java中,实现克隆通常需要重写`Object`类的`clone()`方法。 **浅克隆(Shallow Clone)** 浅克隆创建一个新对象,...

    Java中的克隆(Clone)机制

    - **浅克隆与深克隆的选择**:根据对象中字段的类型来决定使用哪种克隆方式。如果对象中包含引用类型字段,且这些字段的值需要独立,那么应选择深克隆。否则,如果只关心基本类型字段的复制,浅克隆就足够了。 - *...

    Java深复制与浅复制.doc

    1. **浅复制(浅克隆)**: 浅复制是指创建一个新对象,这个新对象包含了原对象的所有字段的值,但对引用类型的字段只是复制了引用,而不是复制引用的对象本身。也就是说,新对象和原对象引用了同一块内存区域。如果...

    java设计模式【之】原型模式、深拷贝与浅拷贝【源码】【场景:克隆羊】

    java设计模式【之】原型模式、深拷贝与浅拷贝【源码】【场景:克隆羊】 * 原型模式(Prototype) * 实现方式: * 需要被克隆的 class类, 重写Object中的clone()方法,并实现Cloneable接口(否则报错 ...

    java 对象克隆

    一、浅克隆与深克隆 在Java中,克隆分为两种类型:浅克隆(Shallow Clone)和深克隆(Deep Clone)。浅克隆创建的新对象只复制了原对象的引用,这意味着如果原对象中包含可变的引用类型,如数组或复杂的数据结构,...

    Java深复制与浅复制&Clone

    在Java编程语言中,对象复制是一个常见的操作,...理解并正确使用浅复制和深复制对于开发高效、健壮的Java应用程序至关重要。在处理复杂的数据结构时,应根据需求选择合适的复制策略,以避免不必要的副作用和数据污染。

    Java对象的复制克隆

    Java中的对象复制与克隆是程序开发中常见的需求,主要用于创建一个对象的副本,而不会影响原始对象的状态。这里主要讨论两种类型的对象拷贝:浅拷贝和深拷贝。 浅拷贝,也称为表面拷贝,是创建一个新的对象,然后将...

    JAVA_对象克隆

    二、浅克隆与深克隆 1. 浅克隆:默认的`clone()`方法执行的是浅克隆,它创建的新对象只复制原始对象的基本数据类型字段,对于引用类型的字段,新对象和原始对象共享同一个引用。这意味着对引用对象的修改会影响到...

    浅拷贝(浅复制、浅克隆)、深拷贝(深复制、深克隆)实战工程

    在编程领域,尤其是在Java语言中,对象的复制是常见的操作,这涉及到两个主要概念:浅拷贝(浅复制、浅克隆)和深拷贝(深复制、深克隆)。这两个概念是理解对象生命周期和内存管理的关键部分。下面将详细阐述它们的...

Global site tag (gtag.js) - Google Analytics