cloning java_Java 克隆

1.为什么要克隆?

新new一个不好吗?new一个的状态是初始值,如果改变了某个属性值,则需要通过相同的改变操作使得new出来的对象和现对象值相同。克隆可直接复制当前对象的任何值。并且初始化可能费时间比较多。克隆有浅克隆和深克隆。

2.浅克隆

需要重写Cloneable接口中的clone()方法。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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都改。

8fbec04e9040e189bfaa3a5b2b65e5e9.png通过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,实现了完全复制。类中类内存也相互独立。

7ffc8c6846e93e8417bbc9216b077c2b.png

简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

用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()方法搞定一切,代码照抄,异常抛出照抄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值