- 重写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 +
'}';
}
}