目錄
克隆與淺克隆區別
深淺克隆的區別在於,能否支持引用類型(包括類、接口、數組等)的成員變量的複製。
淺克隆:對象只複製了它本身和其中包含的值類型的成員變量,引用類型的成員對象並沒有複製。
深克隆:對象本身以及包含的所有成員變量都會被複制。
淺克隆demo
關鍵點:Student類,實現Cloneable接口。
Student中數值類型屬性 name sex,引用類型teacher
public class Student implements Cloneable {
String name;
String sex;
Teacher teacher;
public Student(String name, String sex, Teacher teacher) {
this.name = name;
this.sex = sex;
this.teacher = teacher;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", teacher=" + teacher +
'}';
}
}
/**
* 教師類
*/
public class Teacher {
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public Teacher(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
測試類:
public class LightCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("laoshi", "男");
Student a = new Student("zhangsan", "男", teacher);
Student b = (Student) a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a.equals(b));
System.out.println("b爲a克隆的一個對象,兩者引用不相等");
System.out.println("對克隆的的值屬性修改,a的性別屬性修改爲女");
b.setSex("女");
System.out.println(a);
System.out.println(b);
System.out.println("對克隆的的值屬性修改,被克隆的對象中對應值屬性無變化了");
// 對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
System.out.println("對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi");
a.getTeacher().setName("laosi");
System.out.println(a);
System.out.println(b);
System.out.println("對被克隆的的對象進行屬性修改,克隆的對象中對應屬性也變化了");
// 對b的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
System.out.println("對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu\"");
b.getTeacher().setName("laowu");
System.out.println(a);
System.out.println(b);
System.out.println("對克隆的的對象進行屬性修改,被克隆的對象中對應屬性也變化了");
}
}
運行結果:
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b爲a克隆的一個對象,兩者引用不相等
對克隆的的值屬性修改,a的性別屬性修改爲女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對克隆的的值屬性修改,被克隆的對象中對應值屬性無變化了
對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laosi', sex='男'}}
對被克隆的的對象進行屬性修改,克隆的對象中對應屬性也變化了
對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laowu', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
對克隆的的對象進行屬性修改,被克隆的對象中對應屬性也變化了
可以看出:對象只複製了它本身和其中包含的值類型的成員變量,修改克隆對象的值屬性,不會影響被克隆對象屬性的值,但修改引用會影響被克隆的引用屬性。
深克隆demo
關鍵點:Student類,實現Cloneable接口 重寫clone方法,引用屬性teacher類也需要實現Cloneable接口。
如:Student的重寫clone方法:
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student)super.clone();
student.setTeacher((Teacher)student.getTeacher().clone());
return student;
}
引用屬性teacher類需要實現Cloneable接口。
測試類:
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("laoshi", "男");
Student a = new Student("zhangsan", "男", teacher);
Student b = (Student) a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a.equals(b));
System.out.println("b爲a克隆的一個對象,兩者引用不相等");
System.out.println("對克隆的的值屬性修改,a的性別屬性修改爲女");
b.setSex("女");
System.out.println(a);
System.out.println(b);
System.out.println("對克隆的的值屬性修改,被克隆的對象中對應值屬性未變化");
// 對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
System.out.println("對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi");
a.getTeacher().setName("laosi");
System.out.println(a);
System.out.println(b);
System.out.println("對被克隆的的對象進行屬性修改,克隆的對象中對應屬性未變化");
// 對b的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
System.out.println("對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu\"");
b.getTeacher().setName("laowu");
System.out.println(a);
System.out.println(b);
System.out.println("對克隆的的對象進行屬性修改,被克隆的對象中對應屬性未變化");
}
}
運行結果:
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b爲a克隆的一個對象,兩者引用不相等
對克隆的的值屬性修改,a的性別屬性修改爲女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對克隆的的值屬性修改,被克隆的對象中對應值屬性未變化
對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對被克隆的的對象進行屬性修改,克隆的對象中對應屬性未變化
對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
對克隆的的對象進行屬性修改,被克隆的對象中對應屬性未變化
可以看到手動實現clone方法對引用對象進行克隆,如果引用對象還有引用類型屬性,那麼只能繼續低引用對象進行clone實現。
那麼有沒有更好的方法實現深度克隆呢?
當然有,使用序列化,之需要將Student和Teacher類都實現Serializable接口,Student重寫clone方法。
如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Student implements Serializable {
String name;
String sex;
Teacher teacher;
public Student(String name, String sex, Teacher teacher) {
this.name = name;
this.sex = sex;
this.teacher = teacher;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = null;
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 將流序列化成對象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
student = (Student) ois.readObject();
}catch (Exception e){
System.out.println(e.toString());
}
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", teacher=" + teacher +
'}';
}
}
import java.io.Serializable;
/**
* 教師類
*/
public class Teacher implements Serializable {
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public Teacher(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
使用Serializable接口序列化,引用對象也必須實現Serializable接口。
文末思考
除了使用Serializable接口序列化實現深克隆,還有其他方法嗎?
提示:使用使用Json。