目录
1.默认的clone()方法:实现Cloneable接口,但未对引用类型字段进行特殊处理,默认的super.clone()只会执行浅拷贝。
问题:
刷面经,用博客输出,加深印象,特此记录。
区别:
一图胜千言。
-
浅拷贝 是 创建一个新的对象,基本数据类型 会原模原样复制过去,而引用类型,则是只复制地址,说白了就是和源对象共用一个引用数据对象。
-
深拷贝 是 创建一个独立的副本,基本数据类型 会原模原样复制过去,而引用类型,不复制地址而是生成一个新的地址,但是引用类型的值会复制过去。
实现:
什么时候会浅拷贝
1.默认的clone()方法:实现Cloneable接口,但未对引用类型字段进行特殊处理,默认的super.clone()
只会执行浅拷贝。
class Address {
private String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认的浅拷贝
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address originalAddress = new Address("New York");
Person originalPerson = new Person("Alice", originalAddress);
// 克隆对象
Person clonedPerson = (Person) originalPerson.clone();
// 修改克隆对象的引用类型成员
clonedPerson.getAddress().setCity("San Francisco");
// 输出结果
System.out.println("Original Person: " + originalPerson.getName() + ", " + originalPerson.getAddress().getCity());
System.out.println("Cloned Person: " + clonedPerson.getName() + ", " + clonedPerson.getAddress().getCity());
}
}class Address {
private String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认的浅拷贝
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address originalAddress = new Address("New York");
Person originalPerson = new Person("Alice", originalAddress);
// 克隆对象
Person clonedPerson = (Person) originalPerson.clone();
// 修改克隆对象的引用类型成员
clonedPerson.getAddress().setCity("San Francisco");
// 输出结果
System.out.println("Original Person: " + originalPerson.getName() + ", " + originalPerson.getAddress().getCity());
System.out.println("Cloned Person: " + clonedPerson.getName() + ", " + clonedPerson.getAddress().getCity());
}
}
2.赋值的时候,直接复制了引用类型的地址
import java.io.*;
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Serializable {
String name; // 引用类型
int age; // 基本数据类型
Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 使用序列化实现浅拷贝
public Person shallowCopy() 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);
return (Person) ois.readObject();
}
}
public class Main {
public static void main(String[] args) throws Exception {
Address address = new Address("New York");
Person person1 = new Person("Alice", 30, address);
// 浅拷贝
Person person2 = person1.shallowCopy();
// 输出比较结果
System.out.println(person1 == person2); // 输出:false(两个对象不同)
System.out.println(person1.address == person2.address); // 输出:true(引用相同)
// 修改引用类型字段
person2.address.city = "Los Angeles";
System.out.println(person1.address.city); // 输出:Los Angeles
}
}
3.序列化的时候,引用类型没有序列化
不了解序列化,可以看看。
Java序列化
import java.io.*;
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Serializable {
String name; // 引用类型
int age; // 基本数据类型
Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 使用序列化实现浅拷贝
public Person shallowCopy() 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);
return (Person) ois.readObject();
}
}
public class Main {
public static void main(String[] args) throws Exception {
Address address = new Address("New York");
Person person1 = new Person("Alice", 30, address);
// 浅拷贝
Person person2 = person1.shallowCopy();
// 输出比较结果
System.out.println(person1 == person2); // 输出:false(两个对象不同)
System.out.println(person1.address == person2.address); // 输出:true(引用相同)
// 修改引用类型字段
person2.address.city = "Los Angeles";
System.out.println(person1.address.city); // 输出:Los Angeles
}
}
什么时候会深拷贝
1.自定义clone方法,处理引用数据类型
class Address implements Cloneable {
String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 深拷贝Address对象
}
}
class Person implements Cloneable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) this.address.clone(); // 深拷贝引用类型字段
return cloned;
}
}
2.序列化与反序列化处理引用类型
import java.io.*;
class Address implements Serializable {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Serializable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
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);
return (Person) ois.readObject();
}
}
3.引用第三方工具
引入第三方工具 Apache Commonlangs
使用 SerializationUtils工具类来方便的完成序列化。
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
class Address implements Serializable {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Serializable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
}
public class Main {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("Alice", address);
Person person2 = SerializationUtils.clone(person1); // 深拷贝
}
}
4.手动复制引用类型字段
class Address {
String city;
public Address(String city) {
this.city = city;
}
public Address deepCopy() {
return new Address(this.city); // 手动创建新实例
}
}
class Person {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public Person deepCopy() {
return new Person(this.name, this.address.deepCopy()); // 递归复制引用类型字段
}
}
拓展:
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
/*
为什么ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
这里要byteArrayOutputStream.toByteArray()填这个参数?
因为 ByteArrayOutputStream ByteArrayInputStream 都是从存到内存或者从从内存读写数据
但是二者没有数据共享机制
而且ByteArrayOutputStream是把数据存储到了自己在内存中维护的字节数组里。
ByteArrayInputStream 无法直接访问这个字节数组,需要ByteArrayOutputStream来帮助byteArrayOutputStream.toByteArray()
一个比喻:
ByteArrayInputStream ByteArrayOutputStream是两个水桶,但是ByteArrayOutputStream是满的,
需要把水倒进ByteArrayOutputStream ,而toByteArray()就是盛水的盆。
*/
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object o = objectInputStream.readObject();
/*
在java中, ByteArrayInputStream 和 ByteArrayOutputStream二者是独立的,都遵循单一职责原则,所有没有共享机制
谁也无法访问对方,如果可以随意访问对方,那数据就有可能不一致
toByteArray() 是生成一个独立的副本,来帮助我们访问,这样也不影响ByteArrayOutputStream本身存储的数据。
*/