- 重寫clone() 方法時,需要實現 cloneable接口
- clone()方法可以實現深拷貝,但當需要克隆的對象中包含引用類型的屬性且層級較深時,相關的引用類型都需要實現cloneable接口,且在拷貝的對象中,clone()方法需要做較多的處理,比較麻煩。
- 使用序列化來實現深拷貝時,注意涉及到的對象都需要實現序列化接口。對於不需要序列化的屬性,使用@transient註解。關鍵代碼中涉及
字節數組輸入輸出流
和對象輸入輸出流
。
測試代碼
/**
* @description:
* @author: Yuanye.Wong
* @version: v1.0
* @data: 2019-06-12 23:56
**/
public class Demo {
@Test
// 引用拷貝
public void test1(){
Person p = new Person("小明",20,new Work("coach"));
Person p2 = p;
System.out.println("p == p2 : "+(p==p2));
System.out.println(p);
System.out.println(p2);
/* p == p2 : true
Person{name='小明', age=20, work=Work(1789447862){name='coach'}}
Person{name='小明', age=20, work=Work(1789447862){name='coach'}}
*/
}
/**
* 淺拷貝:創建新對象,對原始對象的屬性值進行完全拷貝,
* 屬性值是基本類型,則複製值;屬性是引用類型,則複製引用(對象的內存地址)。當原始對象中引用類型發生變化,新對象中的屬性也隨之變化。
* 實現cloneable接口,使用clone()來實現對象拷貝。當不存在引用類型的屬性時,則等同深拷貝,對象間互不影響,存在引用類型的屬性時,則爲淺拷貝
*/
// 修改非引用型屬性
@Test
public void test2() throws CloneNotSupportedException {
Dog dog1 = new Dog("1aa");
Dog dog2 = (Dog)dog1.clone();
System.out.println(dog1 == dog2); // false ,不再是一個對象
System.out.println(dog2.toString());// Dog{nickName='1aa'}
dog1.setNickName("1bb");
System.out.println(dog2.toString());// Dog{nickName='1aa'},原始對象非引用型屬性的變化不再影響clone的新對象
}
// clone淺拷貝
@Test
public void test3() throws CloneNotSupportedException {
Dog dog1 = new Dog("1aa");
dog1.setAddress(new Address("SH"));
Dog dog2 = (Dog)dog1.clone();
System.out.println(dog1 == dog2); // false ,不再是一個對象
dog1.setNickName("1bb");
dog1.getAddress().setZone("BJ");// 淺拷貝的對象中,引用型的屬性address指向同一對象,修改address屬性,dog1 dog2的address都會隨之變化。
System.out.println(dog1.getNickName()+dog1.getAddress());// 1bbAddress{zone='BJ'}
System.out.println(dog2.getNickName()+dog2.getAddress());// 1bbAddress{zone='BJ'}
dog1.setAddress(new Address("CC"));// 將address引用指向新的address對象。 dog1 dog2中的address不再指向同一對象
System.out.println(dog1.getNickName()+dog1.getAddress());// 1bbAddress{zone='CC'}
System.out.println(dog2.getNickName()+dog2.getAddress());// 1bbAddress{zone='BJ'}
}
// clone 實現深拷貝
@Test
public void test4() throws CloneNotSupportedException {
Person p1 = new Person("mary",20,new Work("coder"));
Person p2 = (Person) p1.clone();
System.out.println(p1.toString());
System.out.println(p2.toString());
p1.getWork().setName("fighter");
System.out.println(p1.toString());
System.out.println(p2.toString());
/* 結果:
Person{name='mary', age=20, work=Work(1789447862){name='coder'}}
Person{name='mary', age=20, work=Work(38997010){name='coder'}} 對比可知,work對象已經不是一個對象了
Person{name='mary', age=20, work=Work(1789447862){name='fighter'}} 修改引用對象的屬性,不再影響新生成的對象,如下
Person{name='mary', age=20, work=Work(38997010){name='coder'}}
* */
}
// 使用序列化與反序列化進行深拷貝
/**
* 關鍵類:字節數組流、 對象流
* 拷貝的對象以及對象內引用類型的屬性都要實現 Serializable 接口
*/
@Test
public void test5(){
Book book1 = new Book("book1",new Author("mars"));
Book book2 = null;
try {
book2 = (Book)deepCopy(book1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(book1 == book2);
book1.getAuthor().setName("tt");
System.out.println(book1.toString());
System.out.println(book2.toString());
/* 結果:
false
Book{name='book1', author=Author{name='tt'}}
Book{name='book1', author=Author{name='mars'}}
* */
}
// 深拷貝:輸入輸出流處理邏輯
public Object deepCopy(Object obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Object result = ois.readObject();
oos.close();
ois.close();
return result;
}
}
測試用到的相關類
Work
public class Work implements Cloneable{
private String name;
public Work(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Work("+this.hashCode()+"){" +
"name='" + name + '\'' +
'}';
}
}
Person
public class Person implements Cloneable{
private String name;
private int age;
private Work work;
public Person(String name, int age, Work work) {
this.name = name;
this.age = age;
this.work = work;
}
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 Work getWork() {
return work;
}
public void setWork(Work work) {
this.work = work;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person p = (Person) super.clone();
// clone引用類型的值
p.work = (Work) work.clone();
return p;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", work=" + work +
'}';
}
}
Address
public class Address {
private String zone;
public Address(String zone) {
this.zone = zone;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
@Override
public String toString() {
return "Address{" +
"zone='" + zone + '\'' +
'}';
}
}
Dog
public class Dog implements Cloneable{
private String nickName;
private Address address;
public Dog(String nickName) {
this.nickName = nickName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Dog{" +
"nickName='" + nickName + '\'' +
", address=" + address +
'}';
}
}
Author
public class Author implements Serializable {
private String name;
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Author{" +
"name='" + name + '\'' +
'}';
}
}
Book
public class Book implements Serializable {
private String name;
private Author author;
public Book(String name, Author author) {
this.name = name;
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author=" + author +
'}';
}
}