拷貝的實作:
隻有子類實作了Cloneable接口後才可以使用Object類提供的clone方法。
protected native Object clone() throws CloneNotSupportedException;
Cloneable也是一個辨別接口:
public interface Cloneable { }
要想讓對象具有拷貝的功能,必須實作Cloneable接口,并且在類中自定義clone() 調用Object類提供的繼承權限clone方法。
淺拷貝:淺拷貝就是對對象的值(對于基本資料類型而言,值就是内容,對于引用類型,值就是它所指向的位址)進行拷貝。
淺拷貝存在的的問題:牽一發而動全身
隻要任何一個拷貝對象(或原對象)中的引用發生了變化,所有的對象都會受到影響
直接來看個例子:
class Teacher{
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
// 1,實作Cloneable接口
class Student implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
// 2, 自己定義clone()
public Student clone(){
Student student = null;
try {
// 實作拷貝處理
// 産生一個新的student對象,并且拷貝原有所有屬性值
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public static void main(String[] args) {
Teacher teacher = new Teacher("張三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.clone();
System.out.println(student);
System.out.println(studentClone); // 兩個位址不同,有拷貝了新的對象
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName()); // 屬性都相同,拷貝到了結果
System.out.println("---------------------------------------");
// 結果為true,拷貝對象和原對象共同引用teacher
System.out.println(teacher == studentClone.getTeacher());
}
}
從運作結果我們可以看到我們确實拷貝到了student對象的克隆對象studentClone,而且兩者的所有屬性都相同,并且也指向了同一個對象teacher。如下圖。
深拷貝:了解了淺拷貝就不難了解深拷貝了,深拷貝拷貝出來的對象産生了所有引用的新對象。
特點:任何對象的改變不會對其他對象産生影響
深拷貝的實作有兩種方式:
- 遞歸實作深拷貝:如果要拷貝的對象中包含對其他對象的引用,則讓那個被引用的對象所在的類也實作Cloneable接口,和clone方法。
-
序列化實作深拷貝:使用序列化實作深拷貝時,無需再實作Clonable接口,隻需要實作Serializable接口即可,但是clone()還是要寫的。
對序列化有疑問的,可以看java對象的序列化與反序列化
看兩個例子:
遞歸實作深拷貝:
// Student引用了Teacher,Teacher也需要序列化
class Teacher implements Cloneable{
private String name;
private String job;
public Teacher clone(){
Teacher teacher = null;
try {
teacher = (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
class Student implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student clone(){
Student student = null;
try {
student = (Student) super.clone();
// 産生新的引用
student.teacher = this.teacher.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher("張三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.clone();
System.out.println(student);
System.out.println(studentClone);
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName());
System.out.println("---------------------------------------");
System.out.println(teacher == studentClone.getTeacher());
}
}
從結果可以看到,拷貝對象與原對象确實對teacher的引用不同。
序列化實作深拷貝:
import java.io.*;
// 序列化實作深拷貝
class Teacher implements Serializable{
private String name;
private String job;
public Teacher clone(){
Teacher teacher = null;
try {
teacher = (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
class Student implements Serializable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student cloneObject() throws Exception{
Student student = null;
ByteOutputStream bos = new ByteOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//參數是從記憶體中把資料讀進來
ByteArrayInputStream bis = new ByteArrayInputStream(bos.getBytes());
ObjectInputStream ois = new ObjectInputStream(bis);
// 從記憶體中反序列化出來
return (Student) ois.readObject();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test {
public static void main(String[] args) throws Exception{
Teacher teacher = new Teacher("張三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.cloneObject();
System.out.println(student);
System.out.println(studentClone);
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName());
System.out.println("---------------------------------------");
System.out.println(teacher == studentClone.getTeacher());
}
}
其實除了深淺拷貝,還有延遲拷貝,我們可以了解為延遲拷貝就是深拷貝+淺拷貝
演出拷貝在對象隻需要讀取時采用淺拷貝,在需要對值做修改時采用深拷貝。