一 快速回忆
二者需要实现cloneable接口,且重写clone方法,此方法一定要是public 否则你无法使用该克隆对象,并且第一句一定要是super.clone();
浅克隆:
只拷贝对象的基本属性。
引用类型成员变量不拷贝,则克 隆出来的对象与原对象中对应的成员变量指向同样的地址,即克隆对象若改变对应的属性,则原对象对应的属性也会改变 ,这显然不是我们所希望的。
深克隆:
拷贝对象的基本属性,包括其类型,拷贝对象的引用类型。
如果被克隆的对象有引用对象,那么经过深克隆后将产生两个对象,即俩个对象(引用成员变量)所指向的内容不一样。
如果某一个类的成员变量不是原始变量(八大基本类型)或者不可变类(如String、包装类型),必须通过在成员初始化或者get方法时通过深度clone(生成一个不同于原对象成员变量的成员变量)来确保成员变量的不可变性。
当克隆对象只有基本类型时,不含引用类型,可以用浅拷贝实现。
当克隆对象含有引用类型时,必须用深克隆来实现。否则克隆出来的对象还有与原对象相同的引用变量,相互影响,不是我们所希望的。
深克隆具体实现有:
1 深度克隆
引用成员变量所对应的类(暂理解为父类)实现Cloneable接口,且重写clone方法中。包含引用变量所在的类(组合引用成员的类)实现Cloneable接口,重写clone方法,并在其clone方法,加上引用成员变量已重写的clone()方法。具体如下面二中的例子:
p.birth = (Date) this.birth.clone();
2 使用Serializable接口,序列化实现深克隆。
引用成员变量所对应的父类仅实现serializable接口。引用成员变量所在的类(组合引用成员的类)实现serializable、cloneable接口,并重写clone方法。在要实现克隆的方法中,使用ObjectInputStream 、ObjectOutPutStream克隆对象。
序列化方式实现深克隆效率不高,没有直接深度克隆的效率高。
二 详细理解
Object.clone()方法用于对象的拷贝,它会把在堆上的对象所占用的内存空间(地址)拷贝一份然后返回,这样就形成一个新的对象(个人理解)。因为每个对象所占的空间内都有一个指向其类数据的指针,也就是指向方法区中类数据(这个类数据可以通过Class对象进行访问,可以简单、直接的理解为就是指向代表其类的Class对象)。JVM会通过这个指针来判断一个对象的类型,由于把整个对象空间拷贝,所以拷贝的对象的类指针也指向相同的类对象,这就确保了obj.clone().getClass()==obj.getClass(),即它们具有相同的类型。
还有一点,因为只是简单的将对象的空间进行复制,所以如果类具有引用类型的实例变量的话,也只是将这个引用进行拷贝,并不复制其引用的对象。这就导致拷贝对象的引用实例变量与原对象的指向相同的对象,这就是传说中的“浅拷贝”。
如果实例变量引用的对象是不可变的,类似于String,则拷贝对象与原对象不能互相影响,这样的拷贝是成功的。但是如果引用的是可变对象,它们就会影响彼此,对于成功的拷贝而言,这是不允许的。可以对可变的实例变量对象进行特殊处理,以实现拷贝对象和原对象 不能相互影响的“深拷贝”。
由于Object.clone()方法是protected的,所以它只能在lang包中的类或是其子类的方法内部被调用,所以,如果像下面这样调用,会编译出错,在Person kobe_bak=kobe.clone();报错,说clone只能在Object的protected作用域访问。
- class Person implements Cloneable
- {
- private int age;