【Java进阶篇】深拷贝和浅拷贝的理解(保姆级文献)

发布时间:2023年12月26日

在这里插入图片描述

??深拷贝和浅拷贝的理解


在计算机内存中,每个对象都有一个地址,这个地址指向对象在内存中存储的位置。当我们使用变量引用一个对象时,实际上是将该对象的地址赋值给变量。因此,如果我们将一个对象复制到另一个变量中,实际上是将对象的地址复制到了这变量中。


浅拷贝是指将一个对象复制到另一个变量中,但是只复制对象的地址,而不是对象本身。也就是说,原始对象和复制对象实际上是共享同一个内存地址的。因此,如果我们修改了复制对象中的属性或元素,原始对象中对应的属性或元素也会被修改。


读不懂?看图吧,帮助理解。


在这里插入图片描述

在Java中,我们常用的各种BeanUtils基本也都是浅拷贝的。


深拷贝是指将一个对象及其所有子对象都复制到另一个变量中,也就是说,它会创建一个全新的对象,并将原始对象中的所有属性或元素都复制到新的对象中。因此,如果我们修改复制对象中的属性或元素,原始对象中对应的属性或元素不会受到影响。


看图帮助理解:


在这里插入图片描述

比如我们有一个User类,然后他的定义如下:


package com.ifbranch.test;


/**
 * @author 昕宝爸爸爱编程
 * 
 */
public class User {

	private String name;
	private String password;
	private Address address;

	public User(String name, String password, Address address) {
		super();
		this.name = name;
		this.password = password;
		this.address = address;
	}

	public User() {
		super();
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", password=" + password + ", address=" + address + "]";
	}
	
	

}


 class Address {
	private String province;
	private String city;
	private String area;
	public Address() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Address(String province, String city, String area) {
		super();
		this.province = province;
		this.city = city;
		this.area = area;
	}
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getArea() {
		return area;
	}
	public void setArea(String area) {
		this.area = area;
	}
	
	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + ", area=" + area + "]";
	}
	
	
}


当我们基于User类的一个对象user1 拷贝出一个新的对象user2的时候,不管怎么样,user1和user2都是两个不同的对象,他们的地址也不会一样。但是其中的成员变量Address的话可能就因为深浅拷贝的不同而呈现不同的现象喽。


如果是浅拷贝,那么user2中的address会和user1中的address共享同一个地址,当其中一个修改时,另一个也会受影响。

如果是深拷贝,那么user2中的address会和user1中的address并不是同一个地址,当其中一个修改时,另一个是不会受影响的。


??拓展知识仓


??BeanUtils的浅拷贝


我们先举个实际的例子,来看下为什么前面说BeanUtils.copyProperties的过程是浅拷贝。

先来定义两个类:


package com.ifbranch.test;


/**
 * @author 昕宝爸爸爱编程
 * 
 */
public class User {

	private String name;
	private String password;
	private Address address;

	public User(String name, String password, Address address) {
		super();
		this.name = name;
		this.password = password;
		this.address = address;
	}

	public User() {
		super();
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", password=" + password + ", address=" + address + "]";
	}
	
	

}


 class Address {
	private String province;
	private String city;
	private String area;
	public Address() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Address(String province, String city, String area) {
		super();
		this.province = province;
		this.city = city;
		this.area = area;
	}
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getArea() {
		return area;
	}
	public void setArea(String area) {
		this.area = area;
	}
	
	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + ", area=" + area + "]";
	}
	
	
}


然后写一段测试代码:


User user = new User("aidemeng""123456qwe");
user.setAddress(new Address( "shanghai","fengxian","jinhairoad"));

User newUser = new User();
BeanUtils.copyProperties(user, newUser);

System.out.printin(user == newUser);
System.out.println(user.getAddress() == newUser.getAddress());

//输出结果为:

//   false
//   true

即,我们BeanUtils.copyProperties 拷贝出来的newUser是一个新的对象,但是,其中的address对象和原来的user中的address对象是同一个对象。


如果我们修改newUser中的Address对象的值的话,是会同时把user对象中的Address的值也修改了的。可以学着修改下newUser中的address对象:
newUser.getAddress( ).setCity("shanghai") ;
System.out.println(JSON.to]SONString(user));
System.out.println(JSON.to]SONString(newUser));

输出结果为:


{“address”:{“area”:“jinhairoad”,“city”:“shanghai”,“province”:“shanghai”},“name”:“aidemeng”,“password”:“123456qwe”}
{“address”:{“area”:“jinhairoad”,“city”:“shanghai”,“province”:“shanghai”},“name”:“aidemeng”,“password”:“123456qwe”}


??实现Cloneable接口,重写clone()


在Object类中定义了一个clone方法,这个方法其实在不重写的情况下,其实也是浅拷贝的。如果想要实现深拷贝,就需要重写clone方法,而想要重写clone方法,就必须实现Cloneable,否则会报CloneNotSupportedException异常。


将上述代码修改下,重写clone方法:


package com.ifbranch.test;


/**
 * @author 昕宝爸爸爱编程
 * 
 */

public class Address implements Cloneable {
	private String province;
	private String city;
	private String area;
	//省略构造函数和setter/getter


	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}


//------------------------------------------------------

class User implements Cloneable {
	private String name;
	private String password;
	private HomeAddress address;
	//省略构造函数和setter/getter


	@Override
	protected Object clone() throws CloneNotSupportedException {
		User user = (User)super.clone();
		user.setAddress((HomeAddress)address .clone());
		return user;
	}
}

之后,在执行一下上面的测试代码,就可以发现,这时候newUser中的address对象就是一个新的对象了。


这种方式就能实现深拷贝,但是问题是如果我们在User中有很多个对象,那么clone方法就写的很长,而且如果后面有修改,在User中新增属性,这个地方也要改。


那么,有没有什么办法可以不需要修改,一劳永逸呢?


??序列化实现深拷贝


我们可以借助序列化来实现深拷贝。先把对象序列化成流,再从流中反序列化成对象,这样就一定是新的对象了序列化的方式有很多,比如我们可以使用各种JSON工具,把对象序列化成JSON字符串,然后再从字符串中反序列化成对象。

如使用fastjson实现:


User newUser = JSON.parsebject(JSON.toJSONString(user)User.class);

也可实现深拷贝。


除此之外,还可以使用Apache Commons Lang中提供的SerializationUtils工具实现。


我们需要修改下上面的User和Address类,使他们实现Serializable接口,否则是无法进行序列化的。


class User implements Serializable

class Address implements Serializable

然后在需要拷贝的时候:

User newUser = (User) SerializationUtils.clone(user);

同样,也可以实现深拷贝啦~!

文章来源:https://blog.csdn.net/Java_Yangxiaoyuan/article/details/135230951
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。