1.为什么要克隆?
新new一个不好吗?new一个的状态是初始值,如果改变了某个属性值,则需要通过相同的改变操作使得new出来的对象和现对象值相同。克隆可直接复制当前对象的任何值。并且初始化可能费时间比较多。克隆有浅克隆和深克隆。
2.浅克隆
需要重写Cloneable接口中的clone()方法。
packagemy_test;public classTestCopy {public static void main(String[] args) throwsException{
Monkey m1=newMonkey();
m1.setId(66);
Monkey m2=(Monkey) m1.clone();
Monkey m3=(Monkey)m2.clone();
m2.setId(99);
Monkey m4=m1;
System.out.println("m1.id="+m1.getId()+" getClass()="+m1.getClass());
System.out.println("m2.id="+m2.getId()+" getClass()="+m2.getClass());
System.out.println("m3.id="+m3.getId()+" getClass()="+m3.getClass());
System.out.println("m3.id="+m3.getId()+" getClass()="+m4.getClass());
System.out.println(m1==m2);
System.out.println(m1==m4);
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);
System.out.println(m4);
}
}class Monkey implementsCloneable {private intid;public void setId(intid) {this.id =id;
}public intgetId() {returnid;
}public Object clone() throwsCloneNotSupportedException{return super.clone();
}
}
View Code
输出:
m1.id=66 getClass()=class my_test.Monkey
m2.id=99 getClass()=class my_test.Monkey
m3.id=66 getClass()=class my_test.Monkey
m3.id=66 getClass()=class my_test.Monkey
false
true
my_test.Monkey@15db9742
my_test.Monkey@6d06d69c
my_test.Monkey@7852e922
my_test.Monkey@15db9742
m2是通过m1克隆的,m3是通过m2克隆的,m4和m1指向同一个堆内存,再修改m2的值,通过返回toString()方法可知。m1、m2、m3地址各不相同,m1和m4地址相同,修改m2的值,不影响m1和m3,getClass()返回的类型相同。表明通过clone()方法克隆出的对象是与原对象是独立的,开辟了新的堆内存。
在Monkey类里添加引用类型Peach进行测试
packagemy_test;public classTestCopy {public static void main(String[] args) throwsException{
Peach peach=newPeach();
peach.setName("猕猴桃");
Monkey m1=newMonkey();
m1.setId(66);
m1.setP(peach);
Monkey m2=(Monkey)m1.clone();
System.out.println("m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
m1.setId(99);
peach.setName("水蜜桃");
System.out.println("m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
}
}class Monkey implementsCloneable {private intid;privatePeach p;public void setId(intid) {this.id =id;
}public intgetId() {returnid;
}public Object clone() throwsCloneNotSupportedException{return super.clone();
}public voidsetP(Peach p) {this.p=p;
}publicPeach getP() {returnp;
}
}classPeach{privateString name;public voidsetName(String name) {this.name =name;
}publicString getName() {returnname;
}
}
输出:
m1.id=66 m1.p=my_test.Peach@15db9742 p.name=猕猴桃
m2.id=66 m2.p=my_test.Peach@15db9742 p.name=猕猴桃
m1.id=99 m1.p=my_test.Peach@15db9742 p.name=水蜜桃
m2.id=66 m2.p=my_test.Peach@15db9742 p.name=水蜜桃
m2是m1的克隆对象,m1和m2相互独立,但是 类中的引用类peach 是同一片内存,所以peach内容一改,m1和m2中的peach都改。
通过Clone()方法实现的浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;
如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
简单来说,在浅克隆中,当对象被复制时 只 复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。即复制不完全,不够深刻。
3.深克隆
要完完全全深深刻刻地全部复制,也可通过Clone()方法在浅复制中嵌套浅复制。
packagemy_test;public classTestCopy {public static void main(String[] args) throwsException{
Peach peach=newPeach();
peach.setName("猕猴桃");
Monkey m1=newMonkey();
m1.setId(66);
m1.setP(peach);
Monkey m2=(Monkey)m1.clone();
System.out.println("m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
m1.setId(99);
peach.setName("水蜜桃");
System.out.println("m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
}
}class Monkey implementsCloneable {private intid;privatePeach p;public void setId(intid) {this.id =id;
}public intgetId() {returnid;
}public voidsetP(Peach p) {this.p=p;
}publicPeach getP() {returnp;
}public Object clone() throws CloneNotSupportedException{//修改猴子类中的Clone()方法
Monkey monkey=null;try{
monkey=(Monkey)super.clone();//先实现一下浅复制
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
monkey.p=(Peach)p.Clone();//深复制 = 浅复制 套 浅复制
returnmonkey;
}
}class Peach implements Cloneable{//桃子类也实现来实现Clone()方法
privateString name;public voidsetName(String name) {this.name =name;
}publicString getName() {returnname;
}publicObject Clone() {
Peach peach=null;try{
peach= (Peach)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnpeach;
}
}
输出:
m1.id=66 m1.p=my_test.Peach@15db9742 p.name=猕猴桃
m2.id=66 m2.p=my_test.Peach@6d06d69c p.name=猕猴桃
m1.id=99 m1.p=my_test.Peach@15db9742 p.name=水蜜桃
m2.id=66 m2.p=my_test.Peach@6d06d69c p.name=猕猴桃
此时改了m1中的p,不影响m2中的p,实现了完全复制。类中类内存也相互独立。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
用Clone()实现克隆的问题所在:如果多层引用类型,岂不是一层套一层套到天亮,为此有其他方法(序列化)可以实现深克隆。
packagemy_test;import java.io.*;public classTestCopy {public static void main(String[] args) throwsException{
Peach peach=newPeach();
peach.setName("猕猴桃");
Monkey m1=newMonkey();
Monkey m2=null;
m1.setId(66);
m1.setP(peach);try{
m2=(Monkey)m1.deepClone();
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("1 m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("2 m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
m1.setId(99);
peach.setName("水蜜桃");
System.out.println("3 m1.id="+m1.getId()+" m1.p="+m1.getP()+" p.name="+m1.getP().getName());
System.out.println("4 m2.id="+m2.getId()+" m2.p="+m2.getP()+" p.name="+m2.getP().getName());
}
}class Monkey implementsSerializable {private intid;privatePeach p;public void setId(intid) {this.id =id;
}public intgetId() {return this.id;
}public voidsetP(Peach p) {this.p=p;
}publicPeach getP() {return this.p;
}public Object deepClone() throwsIOException,ClassNotFoundException,OptionalDataException {//将对象写入流中
ByteArrayOutputStream bao=newByteArrayOutputStream();
ObjectOutputStream oos=newObjectOutputStream(bao);
oos.writeObject(this);//将对象从流中取出
ByteArrayInputStream bis=newByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=newObjectInputStream(bis);return(ois.readObject());
}
}class Peach implements Serializable{//类中的成员类也需要 实现这个接口
privateString name;public voidsetName(String name) {this.name =name;
}publicString getName() {returnname;
}
}
输出:
m1.id=66 m1.p=my_test.Peach@55f96302 p.name=猕猴桃
m2.id=66 m2.p=my_test.Peach@214c265e p.name=猕猴桃
m1.id=99 m1.p=my_test.Peach@55f96302 p.name=水蜜桃
m2.id=66 m2.p=my_test.Peach@214c265e p.name=猕猴桃
通过序列化实现深克隆,成员的引用类型太多则不需要像Clone()那样实现太多方法。
实现步骤:
需要克隆的类无论是主类还是成员类都要实现Serializable接口
写一个deepClone()方法搞定一切,代码照抄,异常抛出照抄