一 先给出概念定义
浅拷贝:只复制对象的引用,而不是对象本身。也就是说,新引用和原引用指向同一个对象,对其中一个引用所指向对象的修改会影响到另一个引用。
深拷贝:会创建一个新的对象,并且递归地复制原对象的所有属性,新对象和原对象相互独立,对其中一个对象的修改不会影响另一个对象。
二 实际编程中发现的问题
2.1 错误的惯性思维
基本数据类型不受浅拷贝影响,引用数据类型受浅拷贝印象.
错误,错误! 大声把概念定义读三遍!
2.2 例子
public class hello01 {
public static void main(String[] args) {
AliOssProperties aliOssProperties = new AliOssProperties();
aliOssProperties.setEndpoint("endpoint");
aliOssProperties.setAccessKeyId("accessKeyId");
aliOssProperties.setAccessKeySecret("accessKeySecret");
aliOssProperties.setBucketName("bucketName");
List<String> list01 = new ArrayList<>();
list01.add("111");
aliOssProperties.setObject(list01);
AliOssUtil aliOssUtil = new AliOssUtil();
aliOssUtil.setEndpoint(aliOssProperties.getEndpoint());
aliOssUtil.setAccessKeyId(aliOssProperties.getAccessKeyId());
aliOssUtil.setAccessKeySecret(aliOssProperties.getAccessKeySecret());
aliOssUtil.setBucketName(aliOssProperties.getBucketName());
aliOssUtil.setObject(aliOssProperties.getObject());
AliOssUtil aliOssUtil2 = new AliOssUtil();
BeanUtils.copyProperties(aliOssProperties,aliOssUtil2);
System.out.println("aliOssProperties= "+aliOssProperties);
System.out.println("aliOssUtil= "+aliOssUtil);
System.out.println("aliOssUtil2= "+aliOssUtil2);
aliOssProperties.setEndpoint("xxxx");
List<String> list02 = new ArrayList<>();
list02.add("222");
aliOssProperties.setObject(list02);
System.out.println("aliOssProperties= "+aliOssProperties);
System.out.println("aliOssUtil= "+aliOssUtil);
System.out.println("aliOssUtil2= "+aliOssUtil2);
list01.set(0,"333");
System.out.println("aliOssProperties= "+aliOssProperties);
System.out.println("aliOssUtil= "+aliOssUtil);
System.out.println("aliOssUtil2= "+aliOssUtil2);
}
}
输出结果
aliOssProperties= AliOssProperties(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[111])
aliOssUtil= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[111])
aliOssUtil2= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[111])
aliOssProperties= AliOssProperties(endpoint=xxxx, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[222])
aliOssUtil= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[111])
aliOssUtil2= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[111])
aliOssProperties= AliOssProperties(endpoint=xxxx, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[222])
aliOssUtil= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[333])
aliOssUtil2= AliOssUtil(endpoint=endpoint, accessKeyId=accessKeyId, accessKeySecret=accessKeySecret, bucketName=bucketName, object=[333])
2.3 观察
观察输出的第四,第五,第六行,发现aliOssUtil.endpoint和aliOssUtil2.endpoint的值都没有改变.
2.4 结论1-不可变类型不受浅拷贝影响
BeanUtils.copyProperties(aliOssProperties, aliOssUtil2):此方法将 aliOssProperties 对象的属性值复制到 aliOssUtil2 对象。endpoint 是字符串类型,属于引用数据类型,不过字符串在 Java 中是不可变对象。在复制时,会把 aliOssProperties 中 endpoint 属性的值 "endpoint" 复制给 aliOssUtil2 的 endpoint 属性。
aliOssProperties.setEndpoint("xxxx"):这行代码将 aliOssProperties 对象的 endpoint 属性值修改为 "xxxx"。但由于 aliOssUtil2 的 endpoint 属性在之前复制操作后已经有了自己的字符串对象,和 aliOssProperties 的 endpoint 属性指向不同的字符串对象,所以 aliOssUtil2 的 endpoint 属性值不会改变。
不可变类型有String,基本数据类型的包装类,BigDecimal 和 BigInteger, Java 8 引入了 java.time 包,其中的类大多是不可变的,Enum 类型,UUID,自定义不可变类型.
2.5 结论2-引用数据类型有时候,也可以不受浅拷贝影响
如上代码中
第一次 aliOssProperties.setObject(list01);
第二次 aliOssProperties.setObject(list02);
因为aliOssProperties.object指向的对象不同了,所以结果中的第五,第六行中aliOssUtil.object和aliOssUtil2.object的值都没有改变.
2.6 结论3 -BeanUtils.copyProperties()和setter方法都是浅拷贝
代码list01.set(0,"333");执行后,观察结果第八,第九行,发现aliOssUtil.object和aliOssUtil2.object的值都改变了,所以说BeanUtils.copyProperties()和setter方法都是浅拷贝.
三 实现深拷贝操作
3.1 深拷贝实现原理
创建一个新的对象,对于不可变类型的属性,直接同名赋值;对于其他引用类型属性必须递归地赋值,,新对象和原对象相互独立,对其中一个对象的修改不会影响另一个对象。
关键在于递归.
3.2 手动实现深拷贝
import java.util.ArrayList;
import java.util.List;
public class Person {
private String name;
private List<String> hobbies;
public Person(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
// 深拷贝方法
public Person deepCopy() {
// 复制字符串字段(String是不可变对象,直接复制引用即可)
String newName = this.name;
// 复制列表字段(需要创建新的列表并复制内容)
List<String> newHobbies = new ArrayList<>();
for (String hobby : this.hobbies) {
newHobbies.add(hobby); // String是不可变对象,直接添加引用
}
return new Person(newName, newHobbies);
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Person{name='" + name + "', hobbies=" + hobbies + "}";
}
public static void main(String[] args) {
List<String> hobbies = new ArrayList<>();
hobbies.add("Reading");
hobbies.add("Traveling");
Person original = new Person("Alice", hobbies);
// 创建深拷贝副本
Person copy = original.deepCopy();
// 修改原始对象中的字段
original.setName("Bob");
original.getHobbies().set(0, "Gaming");
// 输出结果
System.out.println("Original: " + original);
System.out.println("Copy: " + copy);
}
}
输出结果
Original: Person{name='Bob', hobbies=[Gaming, Traveling]}
Copy: Person{name='Alice', hobbies=[Reading, Traveling]}
3.3 借助clone()方法
默认地clone()方法会产生一个新对象,其属性值是浅拷贝.
自定义的POJO类型,实现implements Cloneable,重写clone()实现深拷贝,从而保证后续代码中可以调用该clone(),实现递归地创建新对象而其属性值是深拷贝得到地.
import java.util.ArrayList;
import java.util.List;
// 定义一个内部类,表示嵌套的引用类型
class InnerClass implements Cloneable {
private String innerData;
public InnerClass(String innerData) {
this.innerData = innerData;
}
public String getInnerData() {
return innerData;
}
public void setInnerData(String innerData) {
this.innerData = innerData;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 调用父类的 clone 方法创建新对象
return super.clone();
}
@Override
public String toString() {
return "InnerClass{" +
"innerData='" + innerData + '\'' +
'}';
}
}
// 定义主类,包含引用类型的属性
class DeepCopyClass implements Cloneable {
private String data;
private InnerClass innerObject;
private List<InnerClass> innerList;
public DeepCopyClass(String data, InnerClass innerObject, List<InnerClass> innerList) {
this.data = data;
this.innerObject = innerObject;
this.innerList = innerList;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public InnerClass getInnerObject() {
return innerObject;
}
public void setInnerObject(InnerClass innerObject) {
this.innerObject = innerObject;
}
public List<InnerClass> getInnerList() {
return innerList;
}
public void setInnerList(List<InnerClass> innerList) {
this.innerList = innerList;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 调用父类的 clone 方法创建新对象
DeepCopyClass clone = (DeepCopyClass) super.clone();
// 对引用类型的属性进行深拷贝
if (this.innerObject != null) {
clone.innerObject = (InnerClass) this.innerObject.clone();
}
if (this.innerList != null) {
clone.innerList = new ArrayList<>();
for (InnerClass item : this.innerList) {
clone.innerList.add((InnerClass) item.clone());
}
}
return clone;
}
@Override
public String toString() {
return "DeepCopyClass{" +
"data='" + data + '\'' +
", innerObject=" + innerObject +
", innerList=" + innerList +
'}';
}
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
InnerClass innerObject = new InnerClass("Inner Data");
List<InnerClass> innerList = new ArrayList<>();
innerList.add(new InnerClass("List Item 1"));
innerList.add(new InnerClass("List Item 2"));
DeepCopyClass original = new DeepCopyClass("Main Data", innerObject, innerList);
// 进行深拷贝
DeepCopyClass clone = (DeepCopyClass) original.clone();
// 修改原始对象的属性
original.getData();
original.getInnerObject().setInnerData("Modified Inner Data");
original.getInnerList().get(0).setInnerData("Modified List Item 1");
// 输出原始对象和克隆对象
System.out.println("Original: " + original);
System.out.println("Clone: " + clone);
}
}
输出结果
Original: DeepCopyClass{data='Main Data', innerObject=InnerClass{innerData='Modified Inner Data'}, innerList=[InnerClass{innerData='Modified List Item 1'}, InnerClass{innerData='List Item 2'}]}
Clone: DeepCopyClass{data='Main Data', innerObject=InnerClass{innerData='Inner Data'}, innerList=[InnerClass{innerData='List Item 1'}, InnerClass{innerData='List Item 2'}]}
3.4 借助序列化
适用于对象及其成员变量都实现了 Serializable 接口的情况,但性能相对较低。
不说了,因为不用.
.
2696

被折叠的 条评论
为什么被折叠?



