目录
一、概念
1、引用数据类型
引用数据类型是指通过定义一个引用类型的变量,可以指向并操作其他的对象实体,引用存放在栈中,实体存放在堆中。常见的引用数据类型有类(class),接口(interface),数据(array),字符串(String),包装类(Integer、Float)。浅拷贝和深拷贝都是对于引用数据类型而言。
2、浅拷贝和深拷贝的区别
(1)浅拷贝
浅拷贝是指会创建一个新对象,但是只复制原始对象的基本类型字段或引用,如果原始对象存在引用类型的字段,拷贝对象和原始对象的引用类型字段会共享同一个实体,对拷贝对象的修改可能会影响原始对象。
(2)深拷贝
深拷贝会递归复制原始对象的所有属性和属性指向的实体,拷贝对象和原始对象各自独立,对拷贝对象的修改不会影响原始对象。
示例图参考:澡澡洗澡澡-一文彻底搞懂深拷贝和浅拷贝的区别
二、通过实现Cloneable接口进行拷贝
1、浅拷贝
实体类需要实现Cloneable接口,并重写clone方法
(1)Address类
package com.xiaobai.java_core.copy;
import java.io.Serializable;
/**
* @Author 王天文
* @Date 2025/1/5 15:55
* @Description:
*/
public class Address implements Serializable, Cloneable {
private static final long serialVersionUID = -2017435981055252655L;
public Address(String city) {
this.city = city;
}
private String city;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
}
(2)Person类
package com.xiaobai.java_core.copy;
import java.io.Serializable;
/**
* @Author 王天文
* @Date 2025/1/5 15:54
* @Description:
*/
public class Person implements Serializable, Cloneable {
private static final long serialVersionUID = 7973985896659216593L;
public Person() {
}
public Person(String name, String sex, Address address) {
this.name = name;
this.sex = sex;
this.address = address;
}
private String name;
private String sex;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 浅拷贝
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Person clone() throws CloneNotSupportedException {
Person personClone = (Person) super.clone();
return personClone;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", address=" + address +
'}';
}
}
(3)测试代码
package com.xiaobai.java_core.copy;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
/**
* @Author 王天文
* @Date 2025/1/5 18:32
* @Description: 浅拷贝和深拷贝测试
*/
@Slf4j
public class CopyTest {
@Test
public void shallowCopy() throws CloneNotSupportedException {
Person person = new Person("张三", "男", new Address("北京"));
Person personCopy = person.clone();
personCopy.getAddress().setCity("上海");
log.info("原始对象:" + person);
log.info("拷贝对象:" + personCopy);
}
}
(4)输出
原始对象:Person{name='张三', sex='男', address=Address{city='上海'}}
拷贝对象:Person{name='张三', sex='男', address=Address{city='上海'}}
可以看到对拷贝对象的修改影响了原始对象,person对象和personCopy对象的address属性的堆内存是共享的。
2、深拷贝
在Person类中添加deepClone方法,在拷贝Person对象的同时也拷贝成员变量address,并赋值给Person的拷贝对象。Address类的代码不变。
(1)Person类代码修改
package com.xiaobai.java_core.copy;
import java.io.Serializable;
/**
* @Author 王天文
* @Date 2025/1/5 15:54
* @Description:
*/
public class Person implements Serializable, Cloneable {
private static final long serialVersionUID = 7973985896659216593L;
public Person() {
}
public Person(String name, String sex, Address address) {
this.name = name;
this.sex = sex;
this.address = address;
}
private String name;
private String sex;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 浅拷贝
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Person clone() throws CloneNotSupportedException {
Person personClone = (Person) super.clone();
return personClone;
}
/**
* 深拷贝
* @return
* @throws CloneNotSupportedException
*/
public Person deepClone() throws CloneNotSupportedException {
Person personClone = (Person) super.clone();
personClone.address = this.address.clone();
return personClone;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", address=" + address +
'}';
}
}
(2)测试代码
@Test
public void deepCopy() throws CloneNotSupportedException {
Person person = new Person("张三", "男", new Address("北京"));
Person personCopy = person.deepClone();
personCopy.getAddress().setCity("上海");
log.info("原始对象:" + person);
log.info("拷贝对象:" + personCopy);
}
(3)输出
原始对象:Person{name='张三', sex='男', address=Address{city='北京'}}
拷贝对象:Person{name='张三', sex='男', address=Address{city='上海'}}
三、浅拷贝和深拷贝工具类
1、浅拷贝
(1)示例代码
使用Spring的org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object)可以实现浅拷贝
package com.xiaobai.java_core.copy;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
/**
* @Author 王天文
* @Date 2025/1/5 19:48
* @Description:
*/
@Slf4j
public class SpringCopyTest {
@Test
public void shallowCopy() {
Person person = new Person("张三", "男", new Address("北京"));
Person personCopy = new Person();
BeanUtils.copyProperties(person, personCopy);
personCopy.getAddress().setCity("上海");
log.info("原始对象:" + person);
log.info("拷贝对象:" + personCopy);
}
}
(2)输出
原始对象:Person{name='张三', sex='男', address=Address{city='上海'}}
拷贝对象:Person{name='张三', sex='男', address=Address{city='上海'}}
2、深拷贝
(1)示例代码
使用org.apache.commons.lang.SerializationUtils实现深拷贝,示例代码如下:
@Test
public void deepCopy() {
Person person = new Person("张三", "男", new Address("北京"));
// 使用SerializationUtils进行深拷贝
Person personCopy = (Person) SerializationUtils.clone(person);
personCopy.getAddress().setCity("上海");
log.info("原始对象:" + person);
log.info("拷贝对象:" + personCopy);
}
(2)输出
原始对象:Person{name='张三', sex='男', address=Address{city='北京'}}
拷贝对象:Person{name='张三', sex='男', address=Address{city='上海'}}