浅复制与深复制
浅复制指的是只复制对象本身的值而当对象中有某个属性是其他对象或者数据的引用时,只复制引用地址。所以浅复制所产生的新对象在原对象引用发生改变时也会发生改变。
反之,深复制指复制对象的属性同时,对其引用的对象也进行复制。这样新对象的引用不随着原对象引用的更改而发生改变。
应用场景:客户端需要获取某个类对象,而且该类对象不能被客户端修改。
在我们日常编程的过程中,经常会遇到A模块向B模块请求获得一个数值或者对象的情况。然后,众所周知,Java中传递数据的方式,分为值传递和引用传递(地址传递)。值传递自然很安全,但是引用传递(地址传递)的时候,A模块可能会对从B模块中获取的对象(或者其他引用传递(地址传递)类型数据)进行恶意修改,从而影响B模块的运行,甚至导致B模块的崩溃,这时候,使用神奇克隆术——Java深、浅Clone,就可以解决这个令人头疼的问题。[1]
浅复制实现
Object类中的clone()方法在默认情况下是浅复制,使用clone方法时需要实现Cloneable接口并实现clone()方法。注意clone()方法应该写在try catch块中。
代码如下:
public class ShallowClone {
public static void main(String[] args) {
School school=new School("武汉大学");
Person_S person=new Person_S(122, "王邦彦", school);
Person_S person2=null;
try {
person2=(Person_S) person.clone();
System.out.println(person2==person ? "深复制": "浅复制");
System.out.println(person);
System.out.println(person2);
school.setSchool("北京大学");
System.out.println(person);
System.out.println(person2);
} catch (Exception e) {
// TODO: handle exception
}
}
}
class Person_S implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
private int id;
private String name;
private School school;
public Person_S(int id,String name,School school) {
this.id=id;
this.name=name;
this.school=school;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return this.name+this.id+this.school.getSchool();
}
}
class School{
private String name;
public School(String name) {
this.name=name;
}
public String getSchool() {
return this.name;
}
public void setSchool(String name) {
this.name=name;
}
}
结果是
我们可以清楚的看到,当改变了Person类对象的引用对象属性时,person2这个浅克隆出来的对象中School引用属性也发生了改变。
深复制的实现
深复制有2种主要实现方式。
第一种是实现Cloneable接口,并重写clone()方法而且引用的对象也需要实现clone()这就像是嵌套一样,每一次clone的时候只是对自己进行完全复制。
代码如下:
class Person_D implements Cloneable {
private String name;
private int age;
private String address;
private School_D school;
public Person_D() {}
public Person_D(String name, int age, String address,School_D school) {
this.name = name;
this.age = age;
this.address = address;
this.school=school;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "姓名是:"+this.name+"年龄:"+this.age+"住址"+this.address+" 所属学校为"+this.school.getSchool();
}
@Override
public Object clone() {
Object object = null;
try {
object=super.clone();
this.school=(School_D) this.school.clone();//此时Person对象中school就更新
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
Person_D newPerson=(Person_D)object;
return newPerson;
}
public School_D getSchool() {
return this.school;
}
}
class School_D implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
private String name;
public School_D(String name) {
this.name=name;
}
public String getSchool() {
return this.name;
}
public void setSchool(String name) {
this.name=name;
}
}
public class DeepCopy {
public static void main(String[] args) throws Exception {
School_D school=new School_D("武汉大学");
Person_D p1=new Person_D("小明", 11, "张家界",school);
Person_D p2=(Person_D) p1.clone();
System.out.println(p1==p2 ? "浅复制":"深复制");
System.out.println(p1);
System.out.println(p2);
p1.getSchool().setSchool("北京大学");
System.out.println(p1);
System.out.println(p2);
}
}
结果是
这时改变引用对象的属性也影响不到新克隆出来的对象。
第二种方式 ,是通过序列化操作进行对象克隆。被克隆的对象及其子对象均需要implement Serializable接口
序列化步骤为:先建立ByteArrayOutputStream 对象 然后交给ObjectOutputStream 对象进行初始化 ObjectOutputStream对象通过.writeObject(Object o)方法将需要序列化的对象进行序列化操作。然后使用flush()方法将缓冲区的数据取出,通过ObjectInputStream 对象将数据重新反序列化,readObject()返回反序列化所产生的对象。
代码如下:
class Person_D implements Serializable{
private String name;
private int age;
private String address;
private School_D school;
public Person_D() {}
public Person_D(String name, int age, String address,School_D s) {
this.name = name;
this.age = age;
this.address = address;
this.school=s;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "姓名是:"+this.name+"年龄:"+this.age+"住址"+this.address+" 学校是"+this.school;
}
public School_D getSchool() {
return this.school;
}
}
class School_D implements Serializable {
private String name;
public School_D(String name) {
this.name=name;
}
public String getSchool() {
return this.name;
}
public void setSchool(String name) {
this.name=name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return this.name;
}
}
public class DeepCopy {
public static void main(String[] args) throws Exception {
School_D school_D=new School_D("武汉大学");
Person_D p1=new Person_D("小明", 11, "张家界",school_D);
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(p1);
objectOutputStream.flush();
ObjectInputStream objectInputStream=new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Person_D p2=(Person_D) objectInputStream.readObject();
System.out.println(p1==p2 ? "浅复制":"深复制");
System.out.println(p1);
System.out.println(p2);
p1.getSchool().setSchool("北京大学");;
System.out.println(p1);
System.out.println(p2);
}
}
结果:
参考资料
[1]https://blog.csdn.net/qiumengchen12/article/details/45022919