1. 概述
Java對象要調用 clone 進行複制,必須實作 Cloneable 接口,否則會抛出
CloneNotSupportedException
的異常。
clone 方法預設是淺複制,如果要實作深複制,那必須在clone方法調用對象成員的複制方法。
- 淺複制:複制對象時,對象内部引用類型的成員,仍然共用記憶體空間。此時A對象修改這個成員的屬性,B對象的該成員也會受影響。
- 深複制:複制對象時,對象内部引用類型的成員,也會重新開辟新的空間。此時A對象和B對象完全互不幹擾。
2. 淺複制clone
Grade: 班級類,實作了Cloneable接口
@Data
public class Grade implements Cloneable{
//名稱
private String name;
//班主任
private Teacher headTeacher;
@Override
public Object clone() throws CloneNotSupportedException {
Grade grade = (Grade) super.clone();
return grade;
}
}
Teacher: 教師類,同樣實作Cloneable接口
@Data
public class Teacher implements Cloneable{
//姓名
private String name;
@Override
public Object clone() throws CloneNotSupportedException {
Teacher teacher = (Teacher) super.clone();
return teacher;
}
}
- 建立教師對象teacher,班級對象grade1
- 從grade1複制出班級對象grade2
- 修改grade1的名稱
- 修改grade1的班主任的姓名
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setName("張三");
Grade grade1 = new Grade();
grade1.setName("一班");
grade1.setHeadTeacher(teacher);
Grade grade2 = (Grade) grade1.clone();
grade1.setName("小一班");
grade1.getHeadTeacher().setName("李四");
System.out.println(grade1);
System.out.println(grade2);
}
看下執行結果
Grade(name=小一班, headTeacher=Teacher(name=李四))
Grade(name=一班, headTeacher=Teacher(name=李四))
- 班級name的修改是互不影響的。上面說了,淺複制是引用類型才公共記憶體空間,String是特殊的引用類型。
- 修改grade1的班主任name,直接影響了grade2的班主任,因為headTeacher就是一個引用類型變量。
3. 深複制clone
修改上面Grade類的clone代碼,複制的時候把Teacher對象也複制一份,這就是深複制。
@Override
public Object clone() throws CloneNotSupportedException {
Grade grade = (Grade) super.clone();
grade.setHeadTeacher((Teacher)this.headTeacher.clone());
return grade;
}
其他代碼不變,然後重新運作程式,輸出如下:
Grade(name=小一班, headTeacher=Teacher(name=李四))
Grade(name=一班, headTeacher=Teacher(name=張三))
可以看到,headTeacher怎麼修改都互不影響,深複制成功。