克隆定义
在 Java 中,克隆是创建原始对象的精确副本的过程。它本质上意味着能够创建一个与原始对象具有相似状态的对象。
复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。
new对象和clone区别
- 使用new操作符创建一个对象
new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,根据类型分配内存,再调用构造函数,填充对象的各个域,这一步就叫对象的初始化。初始化完毕后,可以把他的引用(地址)发布到外部,在外部就可以通过引用操纵这个对象。
- 使用clone方法复制一个对象
clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象一样,然后再使用源对象中对应的各个域,填充新对象的域。同样可以可以把这个新对象的引用发布到外部 。
代码:
public class Student implements Cloneable{
int id;
String name;
Student(int i,String n){
id = i;
name = n;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public static void main(String args[]) throws CloneNotSupportedException {
Student s1 = new Student(111,"Karan");
Student s2 =s1;//s2复制s1的数据
Student s3=(Student) s1.clone();//复制s1的对象
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}
运行结果:
Student@1b6d3586
Student@1b6d3586
Student@4554617c
地址相同,那么肯定是同一个对象。s和s1只是引用而已,他们都指向了一个相同的对象Student(111,“Karan”) 。 这种现象叫做“引用的复制”.
s3是s1的复制,栈内存开辟一个新的地址
浅拷贝
浅拷贝(Shallow Copy of an Object)是 Java 中的“默认实现”。在重写的 clone() 方法中,如果我们没有克隆所有对象类型,下面所有的例子都只是浅拷贝,因为我们没有在 Employee 类的 clone 方法上克隆
代码:
Department 类
public class Department {
private int DepartId;
private String DepartName;
public Department(int departId, String departName) {
DepartId = departId;
DepartName = departName;
}
public int getDepartId() {
return DepartId;
}
public void setDepartId(int departId) {
DepartId = departId;
}
public String getDepartName() {
return DepartName;
}
public void setDepartName(String departName) {
DepartName = departName;
}
}
Employee 类
public class Employee implements Cloneable{
private int id;
private String name;
private Department departemnt;
public Employee(int id, String name, Department departemnt) {
this.id = id;
this.name = name;
this.departemnt = departemnt;
}
//clone
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartemnt() {
return departemnt;
}
public void setDepartemnt(Department departemnt) {
this.departemnt = departemnt;
}
测试类:
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Department dept= new Department(1, "HR");
Employee original = new Employee(1, "Admin", dept);
//创建一个原始对象的克隆
Employee cloned = (Employee) original.clone();
//如果克隆确实有效,使用员工ID进行验证
System.out.println(cloned.getId());//1
//验证JDK的规则
// 必须为真且对象必须有不同的内存地址
System.out.println(original != cloned);//true
//返回如果是同一个类为ture
System.out.println(original.getClass() == cloned.getClass());//true
//默认equals方法检查引用,因此它应该是 false,
System.out.println(original.equals(cloned));//false
//说明有两个同一个对象的引用
System.out.println("-------------------------------");
//修改部门名称
cloned.getDepartemnt().setDepartName("Finance");
//查看元数据和clone后的数据是否发生改变,如果两者数据相同,说明引用的是同一个对象
//如果在数据中clone,对clone的数据修改,元数据也会被修改,说明是浅拷贝
//lone的数据修改,元数据也会被修改,这不是我们愿意看到的,我们想要对clone数据修改不影响元数据,这就是深拷贝
System.out.println(original.getDepartemnt().getDepartName());
System.out.println(cloned.getDepartemnt().getDepartName());
}
}
运行结果:
1
true
true
false
-------------------------------
Finance
Finance
原理
浅拷贝:直接将源对象中的DepartName的引用值拷贝给新对象的DepartName字段;
深拷贝
深拷贝(Deep Copying)在大多数情况下,深度克隆或深度复制是所需的行为。在深拷贝中,我们创建了一个独立于原始对象的克隆,并且在克隆对象中进行更改不应影响原始对象。
代码:
Department类
public class Department implements Cloneable {
private int DepartId;
private String DepartName;
public Department(int departId, String departName) {
DepartId = departId;
DepartName = departName;
}
//Department类添加clone()方法
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
//其余代码相同
Employee 类
public class Employee implements Cloneable{
private int id;
private String name;
private Department departemnt;
public Employee(int id, String name, Department departemnt) {
this.id = id;
this.name = name;
this.departemnt = departemnt;
}
//修改Employee类的clone()方法
// @Override
// protected Object clone() throws CloneNotSupportedException{
// return super.clone();
// }
@Override
protected Object clone() throws CloneNotSupportedException{
Employee cloned = (Employee) super.clone();
cloned.setDepartemnt((Department) cloned.getDepartemnt().clone());
return cloned;
}
//其余代码相同
测试类:
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Department dept= new Department(1, "HR");
Employee original = new Employee(1, "Admin", dept);
//创建一个原始对象的克隆
Employee cloned = (Employee) original.clone();
//修改部门名称
cloned.getDepartemnt().setDepartName("Finance");
//clone数据中部门被修改数据发生改变,但元数据没有发生变化,两者数据是独立的,这就是深拷贝
System.out.println(original.getDepartemnt().getDepartName()); //HR
System.out.println(cloned.getDepartemnt().getDepartName()); //Finance
}
}
运行结果:
HR
Finance
在这里,更改克隆对象的状态不会影响原始对象。
所以深度克隆需要满足以下规则
- 无需单独复制元数据。
- 原始类中的所有成员类都应支持克隆,并且上下文中原始类的克隆方法应在所有成员类上调用 super.clone() 。
- 如果某个成员类不支持克隆,那么在clone方法中,必须创建一个该成员类的新实例,并将其所有属性复制到新的成员类对象中。这个新的成员类对象将被设置在克隆对象中。