java 浅克隆_java对象的浅克隆和深克隆

引言:

在Object基类中,有一个方法叫clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝一份,而浅克隆仅仅是拷贝一份此属性的引用。首先看一下容易犯的几个小问题

clone方法是Object类的,并不是Cloneable接口的,Cloneable只是一个标记接口,标记接口是用用户标记实现该接口的类具有某种该接口标记的功能,常见的标记接口有三个:Serializable、Cloneable、RandomAccess,没有实现Cloneable接口,那么调用clone方法就会爆出CloneNotSupportedException异常。

Object类中的clone方法是protected修饰的,这就表明我们在子类中不重写此方法,就在子类外无法访问,因为这个protected权限是仅仅能在Object所在的包和子类能访问的,这也验证了子类重写父类方法权限修饰符可以变大但不能变小的说法。

protected native Object clone() throws CloneNotSupportedException;

重写clone方法,内部仅仅是调用了父类的clone方法,其实是为了扩大访问权限,当然你可以把protected改为public,以后再继承就不用重写了。当然只是浅克隆的clone函数,深克隆就需要修改了。

@Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone();

}

属性是String的情况,String也是一个类,那String引用类型吗?String的表现有的像基本类型,归根到底就是因为String不可改变,克隆之后俩个引用指向同一个String,但当修改其中的一个,改的不是String的值,却是新生成一个字符串,让被修改的引用指向新的字符串。外表看起来就像基本类型一样。

浅克隆:

浅克隆就是引用类型的属性无法完全复制,类User中包含成绩属性Mark,Mark是由Chinese和math等等组成的,浅克隆失败的例子

classMark{private intchinese;private intmath;public Mark(int chinese, intmath) {this.chinese =chinese;this.math =math;

}public void setChinese(intchinese) {this.chinese =chinese;

}public void setMath(intmath) {this.math =math;

}

@OverridepublicString toString() {return "Mark{" +

"chinese=" + chinese +

", math=" + math +

'}';

}

}public class User implementsCloneable{privateString name;private intage;privateMark mark;public User(String name, intage,Mark mark) {this.name =name;this.age =age;this.mark =mark;

}

@OverridepublicString toString() {return "User{" +

"name='" + name + '\'' +

", age=" + age +

", mark=" + mark +

'}';

}

@Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone();

}public static void main(String[] args) throwsCloneNotSupportedException {

Mark mark= new Mark(100,99);

User user= new User("user",22,mark);

User userClone=(User) user.clone();

System.out.println("原user:"+user);

System.out.println("克隆的user:"+userClone);//修改引用类型的mark属性

user.mark.setMath(60);

System.out.println("修改后的原user:"+user);

System.out.println("修改后的克隆user:"+userClone);

}

}

输出结果为:

原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

修改后的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}

修改后的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}

很清楚的看到user的mark更改后,被克隆的user也修改了。而要想不被影响,就需要深克隆了。

深克隆:

方式一:clone函数的嵌套调用

既然引用类型无法被完全克隆,那将引用类型也实现Cloneable接口重写clone方法,在User类中的clone方法调用属性的克隆方法,也就是方法的嵌套调用

class Mark implementsCloneable{private intchinese;private intmath;public Mark(int chinese, intmath) {this.chinese =chinese;this.math =math;

}public void setChinese(intchinese) {this.chinese =chinese;

}public void setMath(intmath) {this.math =math;

}

@Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone();

}

@OverridepublicString toString() {return "Mark{" +

"chinese=" + chinese +

", math=" + math +

'}';

}

}public class User implementsCloneable{privateString name;private intage;privateMark mark;public User(String name, intage,Mark mark) {this.name =name;this.age =age;this.mark =mark;

}

@OverridepublicString toString() {return "User{" +

"name='" + name + '\'' +

", age=" + age +

", mark=" + mark +

'}';

}

@Overrideprotected Object clone() throwsCloneNotSupportedException {

User user= (User) super.clone();

user.mark= (Mark) this.mark.clone();returnuser;

}public static void main(String[] args) throwsCloneNotSupportedException {

Mark mark= new Mark(100,99);

User user= new User("user",22,mark);

User userClone=(User) user.clone();

System.out.println("原user:"+user);

System.out.println("克隆的user:"+userClone);//修改引用类型的mark属性

user.mark.setMath(60);

System.out.println("修改后的原user:"+user);

System.out.println("修改后的克隆user:"+userClone);

}

}

输出结果为:

原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

修改后的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}

修改后的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

方式二:序列化

上一种方法已经足够满足我们的需要,但是如果类之间的关系很多,或者是有的属性是数组呢,数组可无法实现Cloneable接口(我们可以在clone方法中手动复制数组),但是每次都得手写clone方法,很麻烦,而序列化方式只需要给每个类都实现一个Serializable接口,也是标记接口,最后同序列化和反序列化操作达到克隆的目的(包括数组的复制)。序列化和反序列化的知识请参照下一篇

import java.io.*;class Mark implementsSerializable {private intchinese;private intmath;public Mark(int chinese, intmath) {this.chinese =chinese;this.math =math;

}public void setChinese(intchinese) {this.chinese =chinese;

}public void setMath(intmath) {this.math =math;

}

@OverridepublicString toString() {return "Mark{" +

"chinese=" + chinese +

", math=" + math +

'}';

}

}public class User implementsSerializable{privateString name;private intage;privateMark mark;public User(String name, intage,Mark mark) {this.name =name;this.age =age;this.mark =mark;

}

@OverridepublicString toString() {return "User{" +

"name='" + name + '\'' +

", age=" + age +

", mark=" + mark +

'}';

}public static void main(String[] args) throwsIOException, ClassNotFoundException {

Mark mark= new Mark(100,99);

User user= new User("user",22,mark);

ByteArrayOutputStream bo= newByteArrayOutputStream();

ObjectOutputStream oo= newObjectOutputStream(bo);

oo.writeObject(user);//序列化

ByteArrayInputStream bi = newByteArrayInputStream(bo.toByteArray());

ObjectInputStream oi= newObjectInputStream(bi);

User userClone= (User) oi.readObject();//反序列化

System.out.println("原user:"+user);

System.out.println("克隆的user:"+userClone);

user.mark.setMath(59);

System.out.println("修改后的原user:"+user);

System.out.println("修改后的克隆user:"+userClone);

}

}

输出结果:

原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

修改后的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}

修改后的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

带数组属性的克隆:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

import java.io.*;importjava.util.Arrays;public class User implementsSerializable{privateString name;private intage;private int[] arr;public User(String name, int age, int[] arr) {this.name =name;this.age =age;this.arr =arr;

}

@OverridepublicString toString() {return "User{" +

"name='" + name + '\'' +

", age=" + age +

", arr=" + Arrays.toString(arr) +

'}';

}public static void main(String[] args) throwsIOException, ClassNotFoundException {int[] arr = {1,2,3,4,5,6};

User user= new User("user",22,arr);

ByteArrayOutputStream bo= newByteArrayOutputStream();

ObjectOutputStream oo= newObjectOutputStream(bo);

oo.writeObject(user);//序列化

ByteArrayInputStream bi = newByteArrayInputStream(bo.toByteArray());

ObjectInputStream oi= newObjectInputStream(bi);

User userClone= (User) oi.readObject();//反序列化

System.out.println("原user:"+user);

System.out.println("克隆的user:"+userClone);

user.arr[1] = 9;

System.out.println("修改后的原user:"+user);

System.out.println("修改后的克隆user:"+userClone);

}

}

View Code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值