Java深拷贝与浅拷贝

深拷贝与浅拷贝

浅拷贝

介绍

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

特点

简而言之,当浅拷贝的对象是基本类型时,和复制没有太大的区别,但是如果浅拷贝的对象是一个引用,比如将POJO_A拷贝给POJO_B,使用BeanUtils.copyProperties(POJO_A, POJO_B),如果POJO_A的值发生改变,那POJO_B的值也会变。这个特性在日常正常使用时不会造成什么影响,而且拷贝引用的话可以节省空间。

问题

有一些特殊情况会出现问题,比如说下方这段代码。

void contextLoads() {
		List<UsersInfo> userList = userInfoMapper.selectList(null);
		System.out.println(("----- true data ------"));
		userList.forEach(System.out::println);
		UsersInfo wrongUserInfo = new UsersInfo();
		List<UsersInfo> wrongUserList = new ArrayList<>();
		for (UsersInfo usersInfo : userList) {
			BeanUtils.copyProperties(usersInfo, wrongUserInfo);
			wrongUserList.add(wrongUserInfo);
		}
		System.out.println(("----- wrong data ------"));
		wrongUserList.forEach(System.out::println);
	}

我的预期结果是返回true data中的五组数据,但是实际结果是得到了wrong data 中的五组一样的数据。

得到的结果,wrong data与预期不符
出现这种问题是因为我们使用了BeanUtils.copyProperties()方法,这个方法在底层是一个浅拷贝,它只是拷贝了对象的引用,所以随着usersInfo的变化,引用也一直在变,usersInfo的最后一次引用是Billie的数据,所以在之前的数据也都会变成最后一条数据。具体过程如下图,wrongUserInfo里的东西会随着userInfo的变化而变化,导致wrongUserInfoList里的数据也发生了变化。
当userInfo指向userList[0]时
当userInfo指向userList[4]时
如果要解决这里的问题,就要用到深拷贝了

深拷贝

介绍

深拷贝是在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝

特点

  1. 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
  2. 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。

实现

在Java中,深拷贝的实现有多种方式,我用的方式是使用Gson转为Json数据再转回对象实现深拷贝,代码如下:

@Test
	void contextLoads() {
		List<UsersInfo> userList = userInfoMapper.selectList(null);
		System.out.println(("----- true data ------"));
		userList.forEach(System.out::println);
		UsersInfo wrongUserInfo;
		List<UsersInfo> wrongUserList = new ArrayList<>();
		for (UsersInfo usersInfo : userList) {
			Gson gson = new Gson();
			wrongUserInfo = gson.fromJson(gson.toJson(usersInfo), UsersInfo.class);
			wrongUserList.add(wrongUserInfo);
		}
		System.out.println(("----- wrong data ------"));
		wrongUserList.forEach(System.out::println);
	}

结果如下:
wrong data产生的结果与语气相符
这样我们就修复了使用浅拷贝所出现的问题,但是在日常开发中,还是建议使用浅拷贝,因为深拷贝比浅拷比速度慢并且花销较大。

Java 中,深拷贝浅拷贝是指创建新对象并复制原有对象数据结构的过程,通常用于复杂的数据结构如数组、集合、对象等。它们的区别在于是否完全复制了对象的所有内容,包括嵌套的对象。 **浅拷贝**(Shallow Copy):它只是将原对象的引用复制给新对象。这意味着如果新对象中包含对原对象的引用,那么修改新对象会影响到原对象。在 Java 中,对于基本类型,浅拷贝就是简单地赋值;对于引用类型(如对象),则是复制指向内存地址的引用。 例如: ```java Object obj = new Object(); Object copy = obj; // 浅拷贝,copy 和 obj 引向同一块内存 List list = new ArrayList(); // 对象 List copyList = list; // 浅拷贝,list 和 copyList 都引用同一个ArrayList实例 ``` 修改 `copyList` 的元素会改变 `list`: ```java copyList.add("test"); System.out.println(list); // 输出 "test" ``` **深拷贝**(Deep Copy):则会对整个对象及其所有嵌套对象进行完整的副本操作,包括递归复制嵌套对象。这样改动一个深拷贝不会影响原始对象。在 Java 中,可以手动实现深拷贝,或者使用序列化反序列化的机制,比如 `Cloneable` 接口和 `Object.clone()` 方法(虽然不是所有类都实现了 Cloneable)。 例如: ```java Object obj = new Object(); Object deepCopy = SerializationUtils.deepCopy(obj); // 使用工具类的深拷贝方法 ``` 在这个例子中,`deepCopy` 是一个新的对象,它的状态与 `obj` 完全独立。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值