在Java中,Cloneable
接口是一个标记接口(marker interface),用于指示一个类的实例可以被克隆。要实现浅拷贝和深拷贝,你需要覆盖 Object
类的 clone()
方法,并根据需要执行浅拷贝或深拷贝操作。
浅拷贝:
浅拷贝创建一个新对象,然后将原对象的非静态字段复制到新对象。
如果字段是基本数据类型,复制的是值;
如果是引用类型,复制的是引用。新对象和原对象共享相同的引用对象。
深拷贝:
深拷贝创建一个新对象,同时递归地复制原对象的所有引用对象及其包含的所有对象。
深拷贝独立于原对象,新对象的修改不会影响原对象。
示例代码1
以下是一个简单的示例,演示如何实现浅拷贝和深拷贝:
java
import java.util.ArrayList;
import java.util.List;
class Address {
String city;
Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
int age;
Address address;
Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 深拷贝
public Person deepClone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
// 对引用类型进行独立拷贝
clone.address = new Address(this.address.city);
return clone;
}
}
public class CloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("City");
Person original = new Person("John", 25, address);
// 浅拷贝
Person shallowCopy = (Person) original.clone();
// 深拷贝
Person deepCopy = original.deepClone();
// 修改原对象的引用类型字段
original.address.city = "New City";
// 输出结果
System.out.println("Original: " + original.address.city); // New City
System.out.println("Shallow Copy: " + shallowCopy.address.city); // New City(共享同一个引用对象)
System.out.println("Deep Copy: " + deepCopy.address.city); // City(独立的引用对象)
}
}
在这个例子中,Person
类包含一个引用类型字段 address
,实现了浅拷贝和深拷贝。在深拷贝中,我们对引用类型字段 address
进行了独立拷贝,确保新对象拥有一个独立的 Address
实例。
示例代码2
typescript
package com.linynag.springbootinit.testdemo;
import lombok.Data;
@Data
public class WorkExperience implements Cloneable {
private String company;
private String workTime;
/**
* 实现深拷贝的clone方法
*/
public WorkExperience clone() {
WorkExperience object = null;
try {
object = (WorkExperience) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆异常了");
throw new RuntimeException(e);
}
return object;
}
}
kotlin
package com.linynag.springbootinit.testdemo;
import lombok.Data;
/**
* 简历类
*/
@Data
public class Resume implements Cloneable {
private String name;
private String sex;
private String age;
private WorkExperience workExperience;
public Resume(String name) {
this.name = name;
this.workExperience = new WorkExperience();
}
public void setWorkExperience(String company, String workTime) {
this.workExperience.setCompany(company);
this.workExperience.setWorkTime(workTime);
}
public void showResume() {
System.out.println("姓名:" + this.name + "\t年龄" + this.age + "\t性别" + this.sex
+ "\t工作经历:" + this.workExperience.getCompany() + "\t时间:" + this.workExperience.getWorkTime());
}
/**
* 实现浅拷贝的clone方法
*/
public Resume shallowCopy() {
Resume object = null;
// 使用克隆对象进行克隆内容
try {
object = (Resume) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆异常了");
throw new RuntimeException(e);
}
return object;
}
/**
* 实现深拷贝的clone方法
*/
public Resume deepCopy() {
Resume object = null;
// 使用克隆对象进行克隆内容
try {
object = (Resume) super.clone();
// 对WorkExperience进行深拷贝
object.workExperience = this.workExperience.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆异常了");
throw new RuntimeException(e);
}
return object;
}
}
运行:
java
package com.linynag.springbootinit.testdemo;
public class cloneMainTest {
public static void main(String[] args) {
Resume resume1 = new Resume("18");
resume1.setAge("18");
resume1.setSex("18");
resume1.setWorkExperience("18", "18");
// 浅拷贝
Resume cloneResume1 = resume1.shallowCopy();
cloneResume1.setAge("19");
cloneResume1.setSex("19");
cloneResume1.setWorkExperience("19", "19");
//-------------------------------------------------------------
Resume resume2 = new Resume("20");
resume2.setAge("20");
resume2.setSex("20");
resume2.setWorkExperience("20", "20");
// 深拷贝
Resume cloneResume2 = resume2.deepCopy();
cloneResume2.setAge("21");
cloneResume2.setSex("21");
cloneResume2.setWorkExperience("21", "21");
resume1.showResume();
cloneResume1.showResume();
resume2.showResume();
cloneResume2.showResume();
}
}
运行结果:
Question1:
public WorkExperience clone() 为什么是深拷贝 ?
public Resume shallowCopy() 为什么是浅拷贝 ?
他们应该都是一样的啊 ?
在你提供的代码中,WorkExperience
类实现了 Cloneable
接口,并重写了 clone
方法,该方法使用了 super.clone()
来进行对象的克隆。同样,Resume
类也实现了 Cloneable
接口,并提供了两个方法:shallowCopy
和 deepCopy
,分别用于浅拷贝和深拷贝。
让我们分析一下为什么 WorkExperience
的 clone
方法被称为深拷贝,而 Resume
的 shallowCopy
方法被称为浅拷贝。
WorkExperience 的 clone
方法(深拷贝)
csharp
public WorkExperience clone() {
WorkExperience object = null;
try {
object = (WorkExperience) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆异常了");
throw new RuntimeException(e);
}
return object;
}
在这个方法中,super.clone()
调用会创建一个新的 WorkExperience
对象,并将其字段值设置为与原始对象相同。这是一个浅拷贝的起点。然而,由于 WorkExperience
类实现了 Cloneable
接口,你可以对 workExperience
字段进行深拷贝,确保两个 WorkExperience
对象之间没有共享引用。在这里,WorkExperience
类中的 clone
方法确保了对象的深拷贝。
Resume 的 shallowCopy
方法(浅拷贝)
csharp
public Resume shallowCopy() {
Resume object = null;
try {
object = (Resume) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆异常了");
throw new RuntimeException(e);
}
return object;
}
与 WorkExperience
类不同,Resume
类的 shallowCopy
方法没有明确对其内部的 workExperience
字段进行深拷贝。它只是调用了 super.clone()
来创建一个新的 Resume
对象,并复制了基本数据类型的字段。然而,由于 workExperience
是一个引用类型,这意味着新对象和原对象都引用相同的 WorkExperience
实例,而不是创建一个新的 WorkExperience
实例。因此,这是一个浅拷贝,因为两个对象共享相同的 WorkExperience
引用。
总结起来,WorkExperience
的 clone
方法是深拷贝,因为它明确对引用类型的字段进行了克隆。而 Resume
的 shallowCopy
方法是浅拷贝,因为它没有对其引用类型字段进行深拷贝,导致新对象和原对象共享相同的引用。
Question2:
object.workExperience = this.workExperience.clone();
为什么加了这样一串代码就可以 实现深拷贝 原理是什么?
这一串代码实现了对 Resume
类的深拷贝。让我们一步步解释这段代码的原理:
ini
object.workExperience = this.workExperience.clone();
this.workExperience
: 获取当前对象的WorkExperience
成员变量,即获取原对象的工作经历实例。this.workExperience.clone()
: 调用WorkExperience
类的clone()
方法。因为WorkExperience
类实现了Cloneable
接口并提供了相应的clone()
方法,这个方法会创建一个新的WorkExperience
实例,并将原对象的字段值复制到新的实例中。这是WorkExperience
类中的浅拷贝。object.workExperience = this.workExperience.clone()
: 将新创建的WorkExperience
实例赋值给克隆对象object
的workExperience
成员变量。这样,object
和原对象都拥有了独立的WorkExperience
实例。
这段代码的关键在于 this.workExperience.clone()
的调用,它确保了对 WorkExperience
对象的引用类型字段进行了深拷贝,而不仅仅是复制了引用。因此,object
和原对象都有一个独立的 WorkExperience
实例,修改其中一个对象的工作经历不会影响另一个对象。
需要注意的是,为了使这段代码正常工作,确保 WorkExperience
类中的 clone()
方法也实现了深拷贝,即对其引用类型字段进行了递归复制。如果 WorkExperience
类中有其他引用类型字段,也需要在相应的类中实现深拷贝。这样逐层递归,确保所有引用类型字段都得到了独立的复制。这是实现深拷贝的基本原理。