对象拷贝是指一个对象的值复制到另一个对象中,使得两个对象的值相同,但是他们的对象不同,
在Java中,对象拷贝分为:
注意
- 需要实现Cloneable接口
- 用于指示一个类可以克隆,克隆是创建具有与现有对象相同状态得新对象得过程,
- 接口中没有任何方法,但充当了一个标记接口,指示该类可以克隆
- 需要重写Object父类中clone()方法
- 该方法用来创建对象的副本。
package com.sin.copy;
/**
* Cloneable接口用于指示一个类可以克隆,克隆是创建具有与现有对象相同状态得新对象得过程,
* Cloneable接口中没有任何方法,但充当了一个标记接口,指示该类可以克隆
*/
public class Address implements Cloneable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
/**
* 重写父类Object类中得clone方法
* 该方法用来创建对象的副本。
* @return 调用super.cloue()方法来创建对象的浅拷贝
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.sin.copy;
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CopyDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("西安", "雁塔");
Person person1 = new Person("张三", 18, address);
/**
* 创建一个名为person2的Person类的实例,并在执行浅拷贝后将person1的数据复制给它
* 浅拷贝意味着新对象person2的基本数据类型和对象引用的数据与person1相同,但对象引用将指向与person1相同的内存对象。
* 因此,对person1和person2引用的对象所做的任何更改都将反映在这两个对象中,
*/
Person person2 = (Person) person1.clone();
//==运算符比较内存地址,而不是值。返回结果为false,显然两个对象得地址不同
System.out.println(person1 == person2); // false
//当克隆person1创建person2时,这两个对象具有不同得内存地址,但他们得Address对象共享相同得地址,所有返回结果为true
System.out.println(person1.getAddress() == person2.getAddress()); // true
}
}
深拷贝可以通过使用Java的序列化和反序列化实现。序列化是将对象转换为字节流,反序列化是将字节流转换为对象。
package com.sin.copy;
import java.io.Serializable;
public class Address implements Serializable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
package com.sin.copy;
import java.io.*;
public class Person implements Serializable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 使用Java的序列化和反序列化实现了深拷贝,即复制对象的基本类型数据和引用类型数据本身,而不是复制引用类型数据的地址。
* 将this对象写入一个ByteArrayOutputStream中,然后将这个字节数组输入到一个ObjectInputStream中,最后返回反序列化后的Person对象。
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Person deepCopy() throws IOException, ClassNotFoundException {
// 创建一个字节数组输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 创建一个对象输出流,并将其连接到字节数组输出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 将当前对象写入对象输出流中
oos.writeObject(this);
// 创建一个字节数组输入流,并将其连接到字节数组输出流的字节数组中
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// 创建一个对象输入流,并将其连接到字节数组输入流
ObjectInputStream ois = new ObjectInputStream(bis);
// 从对象输入流中读取对象,并将其转换为 Person 类型并返回
return (Person) ois.readObject();
}
}
package com.sin.copy;
import java.io.IOException;
public class CopyDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("西安", "雁塔");
Person person1 = new Person("张三", 18, address);
//使用deepCopy()方法执行person1到person2的深度复制
//deepCopy()方法使用Java的序列化和反序列化来创建Person的新实例,该实例具有与原始实例相同的值。
Person person2 = person1.deepCopy();
//因为person1和person2是两个独立的实例,所以第一个比较返回false
System.out.println(person1 == person2); // false
//由于deepCopy()方法为person2创建了一个新的Address实例,因此第二次比较也返回false
System.out.println(person1.getAddress() == person2.getAddress()); // false
}
}
延迟拷贝可以通过使用Java的序列化和反序列化实现。序列化是将对象转换为字节流,反序列化是将字节流转换为对象。
package com.sin.copy;
import java.io.Serializable;
public class Address implements Serializable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
package com.sin.copy;
import java.io.*;
public class Person implements Serializable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 使用Java的序列化和反序列化实现了深拷贝,即复制对象的基本类型数据和引用类型数据本身,而不是复制引用类型数据的地址。
* 将this对象写入一个ByteArrayOutputStream中,然后将这个字节数组输入到一个ObjectInputStream中,最后返回反序列化后的Person对象。
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Person deepCopy() throws IOException, ClassNotFoundException {
// 创建一个字节数组输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 创建一个对象输出流,并将其连接到字节数组输出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 将当前对象写入对象输出流中
oos.writeObject(this);
// 创建一个字节数组输入流,并将其连接到字节数组输出流的字节数组中
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// 创建一个对象输入流,并将其连接到字节数组输入流
ObjectInputStream ois = new ObjectInputStream(bis);
// 从对象输入流中读取对象,并将其转换为 Person 类型并返回
return (Person) ois.readObject();
}
}
package com.sin.copy;
import java.io.IOException;
public class CopyDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("西安", "雁塔");
Person person1 = new Person("张三", 18, address);
//使用deepCopy()方法执行person1到person2的深度复制
//deepCopy()方法使用Java的序列化和反序列化来创建Person的新实例,该实例具有与原始实例相同的值。
Person person2 = person1.deepCopy();
//因为person1和person2是两个独立的实例,所以第一个比较返回false
System.out.println(person1 == person2); // false
//由于deepCopy()方法为person2创建了一个新的Address实例,因此第二次比较也返回false
System.out.println(person1.getAddress() == person2.getAddress()); // false
}
}
深拷贝和延迟拷贝的代码看起来相同的原因是它们都涉及复制对象的基本数据类型和引用数据类型。
区别在于为引用数据类型复制的内容。在延迟拷贝中,只复制引用数据类型的地址,而在深度复制中,复制引用数据类型本身。