使用场景:
在日常的编程过程 中,经常会遇到,有一个对象OA,在某一时间点OA中已经包含了一些有效值 ,此时可能会需一个和OA完全相对的新对象OB,并且要在后面的操作中对OB的任何改动都不会影响到OA的值,也就是OA与Ob是需要完全两个独立的对象。
但OB的初始值是由对象OA确定的。在JAVA语言中,用普通的赋值语句是满足不了需求的。使用对象的clone()方法是实现克隆的最简单、也是最高效的手段。
Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。 实现克隆可以用深拷贝和浅拷贝来实现。
深拷贝和浅拷贝的基本概念的理解:
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
通过实例看深拷贝和浅拷贝的实现和不同:
浅拷贝
class AddressNew implementsCloneable {privateString add;publicString getAdd() {returnadd;
}public voidsetAdd(String add) {this.add =add;
}public Object clone() throwsCloneNotSupportedException{return super.clone();
}
}public class StudentNew implementsCloneable{private intnumber;privateString name;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}privateAddressNew addr;publicAddressNew getAddr() {returnaddr;
}public voidsetAddr(AddressNew addr) {this.addr =addr;
}public intgetNumber() {returnnumber;
}public void setNumber(intnumber) {this.number =number;
}public Object clone() throwsCloneNotSupportedException {return super.clone();
}
}public classJavaShallowCopy {public static void main(String [] args) throwsException{
AddressNew addr= newAddressNew();
addr.setAdd("杭州市");
StudentNew stu1= newStudentNew();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);
StudentNew stu2=(StudentNew)stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" +stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" +stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");//stu1.setNumber(20);//stu2.setName("s2");
System.out.println("学生1:" + stu1.getName() + ",地址:" +stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" +stu2.getAddr().getAdd());
}
}
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.StudentNew@133314b
com.songidea.StudentNew@b1bc7ed
学生1:s1,地址:西湖区
学生2:s1,地址:西湖区
从运行结果来看,stu1,stu2的解是2个不同的对象了,但是在改变了,addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值都改变了,也就是说stu1和stu2的addr对象引用的是同一个地址,这个不是我们想要的结果,在实际的开发工作中这一块一定要特别注意,使用不当可能会使业务功能或数据造成错误或混乱,所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看需要实现深拷贝才能达到我们想要的结果,下面会通过实例来看深拷贝的2种不同的实现。
深拷贝:
class Address implementsCloneable {privateString add;publicString getAdd() {returnadd;
}public voidsetAdd(String add) {this.add =add;
}
@OverridepublicObject clone() {
Address addr= null;try{
addr= (Address)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnaddr;
}
}public class Student implementsCloneable{private intnumber;privateString name;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}privateAddress addr;publicAddress getAddr() {returnaddr;
}public voidsetAddr(Address addr) {this.addr =addr;
}public intgetNumber() {returnnumber;
}public void setNumber(intnumber) {this.number =number;
}
@OverridepublicObject clone() {
Student stu= null;try{
stu= (Student)super.clone(); //浅复制
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
stu.addr= (Address)addr.clone(); //深度复制
returnstu;
}
}public classjavaDeepCopy {public static voidmain(String args[]) {
Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);
Student stu2 = (Student) stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");
stu1.setNumber(20);
System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
} }
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.Student@133314b
com.songidea.Student@b1bc7ed
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市
从运行结果来看,stu1,stu2的解是2个不同的对象了,在改变了addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值只有stu1r 改变了,也就是说stu1和stu2的addr对象引用的不是同一个地址,这个是我们想要的结果所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看,这种方式实现了深拷,达到了我们想要的结果,下面会通过实例来看深拷贝的另一种实现:通过序例化来实现深拷贝。
深拷贝序列化的实现:
class AddressSerial implementsSerializable {privateString add;publicString getAdd() {returnadd;
}public voidsetAdd(String add) {this.add =add;
}
}public class StudentSerial implementsSerializable{private intnumber;privateString name;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}privateAddressSerial addr;publicAddressSerial getAddr() {returnaddr;
}public voidsetAddr(AddressSerial addr) {this.addr =addr;
}public intgetNumber() {returnnumber;
}public void setNumber(intnumber) {this.number =number;
}public Object clone() throwsCloneNotSupportedException {return super.clone();
}public Object deepClone() throwsIOException,OptionalDataException,ClassNotFoundException{
ByteArrayOutputStream bo=newByteArrayOutputStream();
ObjectOutputStream oo=newObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi=newByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=newObjectInputStream(bi);returnoi.readObject();
}
}public classjavaDeepCopySreial {public static void main(String [] args) throwsException{
AddressSerial addr= newAddressSerial();
addr.setAdd("杭州市");
StudentSerial stu1= newStudentSerial();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);
StudentSerial stu2=(StudentSerial)stu1.deepClone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" +stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" +stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");//stu1.setNumber(20);//stu2.setName("s2");
System.out.println("学生1:" + stu1.getName() + ",地址:" +stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" +stu2.getAddr().getAdd());
}
}
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.StudentSerial@1e80bfe8
com.songidea.StudentSerial@5e9f23b4
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市
从运行的结果看序例化也达到了深拷贝的场景。
参考和阅读的几偏深拷贝浅拷贝的文章:
https://www.cnblogs.com/null00/archive/2010/12/14/2065088.html
https://www.cnblogs.com/xuanxufeng/p/6558330.html
https://blog.csdn.net/baiye_xing/article/details/71788741