深克隆和浅克隆的区别

赋值操作

@Getter
@Setter
public class User {
	private String name;
	private Address address;
}
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Address {
	private String city;
}

新对象通过复制原始对象得到

public class TestClone {

	public static void main(String[] args) {
		Address address = new Address();
		address.setCity("BJ");

		User user1 = new User();
		user1.setName("zhangsan");
		user1.setAddress(address);

		User user2 = user1; //赋值

		System.out.println("user1: " + user1.getName() + "," + user1.getAddress().getCity());
		System.out.println("user2: " + user2.getName() + "," + user2.getAddress().getCity());
		System.out.println("user是否相等:" + (user1 == user2)); //是同一个地址
		System.out.println("=====修改原始对象=====");

		user1.setName("lisi");
		user1.getAddress().setCity("SH");
		System.out.println("user1: " + user1.getName() + "," + user1.getAddress().getCity());
		System.out.println("user2: " + user2.getName() + "," + user2.getAddress().getCity());

	}
}

输出结果:
user1和user2就是同一个对象
新对象user2中的基本类型和引用类型的属性都会随user1中的变化而变化

user1: zhangsan,BJ
user2: zhangsan,BJ
user是否相等:true
=====修改原始对象=====
user1: lisi,SH
user2: lisi,SH

浅克隆

基本类型的属性复制值, 引用类型的属性复制地址

克隆: 实现 Cloneable 接口, 重写Object类中的 clone() 方法

类User实现clone()方法

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User implements Cloneable {
	private String name;
	private Address address;

	@Override
	public User clone() {
		User clone = null;
		try {
			clone = (User)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}
}

新对象通过克隆原始对象得到

public class TestClone {

	public static void main(String[] args) {
		Address address = new Address();
		address.setCity("BJ");

		User user1 = new User();
		user1.setName("zhangsan");
		user1.setAddress(address);

		User user2 = user1.clone(); //克隆对象

		System.out.println("user1: " + user1.getName() + "," + user1.getAddress().getCity());
		System.out.println("user2: " + user2.getName() + "," + user2.getAddress().getCity());
		System.out.println("user是否相等:" + (user1 == user2));
		System.out.println("=====修改原始对象=====");

		user1.setName("lisi");
		user1.getAddress().setCity("SH");
		System.out.println("user1: " + user1.getName() + "," + user1.getAddress().getCity());
		System.out.println("user2: " + user2.getName() + "," + user2.getAddress().getCity());

	}

测试结果:
对象地址不一样, user1和user2已经不是同一个对象了
新对象中的基本类型的属性name不受user1的影响
引用类型属性会随着user1中的变化而变化, 因为二者的引用类型属性指向的还是同一个地址

user1: zhangsan,BJ
user2: zhangsan,BJ
user是否相等:false
=====修改原始对象=====
user1: lisi,SH
user2: zhangsan,SH

深克隆

基本类型属性和引用类型属性都和原始对象中的完全独立

引用类型属性的类Address也实现 clone() 方法

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Address implements Cloneable {
	private String city;

	@Override
	public Address clone() {
		Address address = null;
		try {
			address = (Address) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return address;
	}

}

User中的clone()方法中调用引用类型属性的克隆

@Getter
@Setter
public class User implements Cloneable {
	private String name;
	private Address address;

	@Override
	public User clone() {
		User clone = null;
		try {
			clone = (User)super.clone();
			//引用类型属性的克隆
			Address newAddress = address.clone();
			clone.setAddress(newAddress);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}
}

测试结果:
新旧对象的地址不一样
user2的基本类型属性name 和 引用类型属性address 都不受原始对象的影响

user1: zhangsan,BJ
user2: zhangsan,BJ
user是否相等:false
=====修改原始对象=====
user1: lisi,SH
user2: zhangsan,BJ

常见的深克隆方式

1. 所有引用属性都实现克隆,整个对象就变成了深克隆。

上面的深克隆示例就是这种

2. 使用 JDK 自带的字节流序列化和反序列化对象实现深克隆。

//类必须实现Serializable接口
public class Address implements Serializable {/*...*/}
public class User implements Serializable {/*...*/}
//深克隆 java.io.*
User user2 = null;
try {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(user1);

    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bais);
    user2 = (User) ois.readObject();

} catch (IOException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

3. 使用第三方工具实现深克隆

比如 Apache Commons Lang。

4. 使用 JSON 工具

如 GSON、FastJSON、Jackson 序列化和反序列化对象实现深克隆。

    //fastjson
   	String str = JSONObject.toJSONString(user1);
	User user2 = JSONObject.parseObject(str, User.class);

参考

浅克隆和深克隆有什么区别?
https://www.cnblogs.com/javacn123/p/17407411.html

什么是深克隆,浅克隆?(案例详解)
https://www.cnblogs.com/blessing2022/p/16622041.html

浅克隆和深克隆
https://blog.csdn.net/ChineseSoftware/article/details/122942803

posted @ 2023-08-22 11:09  theSummerDay  阅读(16)  评论(0编辑  收藏  举报