本文梳理一下Java
中的对象复制。
我们可以发现在Object
类中有如下方法:
protected native Object clone() throws CloneNotSupportedException;
那么如何使用它来实现对象的复制呢?
Shallow & Deep Clone
我们先准备一下我们需要的类:
@Data
@AllArgsConstructor
public class Employee {
private int id;
private String name;
private Department department;
}
@Data
@AllArgsConstructor
public class Department {
private int id;
private String name;
}
然后,让Employee
实现Cloneable
接口,并重写clone()
方法。
@Data
@AllArgsConstructor
public class Employee implements Cloneable{
private int id;
private String name;
private Department department;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
接着,我们创建一个原始对象original
和复制对象cloned
,并修改cloned
的部门名称:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
var cloned = (Employee) original.clone();
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Finance
// Cloned department name: Finance
与我们的期望不一致,我们期望只将cloned
的部门名称改成Finance
,而original
的部门名称仍为Technical
。
这就是Shallow Copy
,浅复制。
来看下示意图:
那么,如何达到我们的期望?
也就是如何做到Deep Copy
?下面提供三种解决方案:
- 所有相关对象均实现
Cloneable
接口 - 复制构造器
- 序列化:
common-lang3
、Jackson
、Gson
示意图如下:
Cloneable
注意,Employee
和Department
类都需要实现Cloneable
接口,并重写clone()
方法。
@Data
@AllArgsConstructor
public class Employee implements Cloneable{
private int id;
private String name;
private Department department;
@Override
public Object clone() throws CloneNotSupportedException {
Employee e = (Employee) super.clone();
e.setDepartment((Department) this.department.clone());
return e;
}
}
@Data
@AllArgsConstructor
public class Department implements Cloneable {
private int id;
private String name;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
var cloned = (Employee) original.clone();
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Technical
// Cloned department name: Finance
Copy Constructor
关键在于将原始对象作为参数传入该类的构造器,对于基本类型和字符串直接赋值,
对于引用类型则需要创建一个新的对象。
@Data
@AllArgsConstructor
public class Employee {
private int id;
private String name;
private Department department;
public Employee(Employee e){
this(e.getId(), e.getName(), new Department(e.getDepartment()));
}
}
@Data
@AllArgsConstructor
public class Department {
private int id;
private String name;
public Department(Department d){
this(d.getId(), d.getName());
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
var cloned = new Employee(original);
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Technical
// Cloned department name: Finance
common-lang3
注意Employee
和Department
类都要实现Serializable
接口。
@Data
@AllArgsConstructor
public class Employee implements Serializable {
private int id;
private String name;
private Department department;
}
@Data
@AllArgsConstructor
public class Department implements Serializable {
private int id;
private String name;
}
public class Test {
public static void main(String[] args) {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
Employee cloned = SerializationUtils.clone(original);
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Technical
// Cloned department name: Finance
Jackson
注意Employee
和Department
类都要有一个默认构造函数。
@Data
@AllArgsConstructor
public class Employee {
private int id;
private String name;
private Department department;
public Employee(){ }
}
@Data
@AllArgsConstructor
public class Department {
private int id;
private String name;
public Department(){}
}
public class Test {
public static void main(String[] args) throws JsonProcessingException {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
ObjectMapper om = new ObjectMapper();
Employee cloned = om.readValue(om.writeValueAsString(original), Employee.class);
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Technical
// Cloned department name: Finance
Gson
对于Model
不需要做任何修改!
@Data
@AllArgsConstructor
public class Employee {
private int id;
private String name;
private Department department;
}
@Data
@AllArgsConstructor
public class Department {
private int id;
private String name;
}
public class Test {
public static void main(String[] args) {
Department dept = new Department(1, "Technical");
Employee original = new Employee( 24, "yyc", dept);
Gson gson = new Gson();
Employee cloned = gson.fromJson(gson.toJson(original), Employee.class);
System.out.println("Cloned id : " + cloned.getId());
System.out.println("Cloned name : " + cloned.getName());
System.out.println("Cloned department id: " + cloned.getDepartment().getId());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
System.out.println("-------------------------------------");
cloned.getDepartment().setName("Finance");
System.out.println("Original department name: " + original.getDepartment().getName());
System.out.println("Cloned department name: " + cloned.getDepartment().getName());
}
}
/** ------------------------------------*/
// Cloned id : 24
// Cloned name : yyc
// Cloned department id: 1
// Cloned department name: Technical
// -------------------------------------
// Original department name: Technical
// Cloned department name: Finance
// Process finished with exit code 0
Reference
一位喜欢提问、尝试的程序员
(转载本站文章请注明作者和出处 姚屹晨-yaoyichen)