在java中,提供了两种实现深克隆的方法,一种是采用序列化的方式实现,另一种是采用依次克隆各个可变的引用类型域的方式实现,但是序列化的效率并不理想。下面通过简单的例子进行对比:
编写类worker,在类中定义了两个域,name和age。在构造方法中初始化这两个域。并提供了get()和set()方法用于获得和修改这两个域。重写了toString()方便对象的输出,重写了clone()方法使用父类的clone()方法实现深克隆:
public class Worker implements Cloneable{
private String name;
private int age;
public Worker(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Worker clone(){
Worker worker = null;
try {
worker = (Worker)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return worker;
}
}
编写Employee类,定义两个域,name和age,在构造方法中初始化这两个域,并提供get()和set()方法用于获得和修改这两个域,再重写toString()方法用于对象的输出,重写clone()方法使用序列化的方式实现深克隆:
public class Employee implements Cloneable,Serializable {
private static final long serialVersionUID = 5139250832033069055L;
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Employee clone(){
Employee employee = null;
// 使用序列化实现深克隆
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);// 将对象写入到字节数组中
oos.close(); // 关闭流
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayInputStream bas = new ByteArrayInputStream(bos.toByteArray());
try {
ObjectInputStream ois = new ObjectInputStream(bas);// 从字节数组中读取对象
try {
employee = (Employee)ois.readObject();
ois.close(); // 关闭流
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return employee;
}
}
编写测试类来进行测试下,并分别输出采用两种方式创建十万个对象所花费的时间,代码如下:
@Test
public void Test(){
List<Employee> employeeList = new ArrayList<>();
Employee employee = new Employee("章子怡",20);
/***************使用序列化的方式实现克隆*****************/
System.out.println( "序列化 开始时间 ==> " +LocalTime.now());
for (int i = 0; i < 100000; i++) {
employeeList.add(employee.clone());
}
System.out.println( "序列化 结束时间 ==> " +LocalTime.now());
List<Worker> workerList = new ArrayList<>();
Worker worker = new Worker("汪峰",25);
/***************使用复制域的方式实现克隆*****************/
System.out.println( "复制域 开始时间 ==> " +LocalTime.now());
for (int i = 0; i < 100000; i++) {
workerList.add(worker.clone());
}
System.out.println( "复制域 结束时间 ==> " +LocalTime.now());
}
结果如下图:
总结:
通过截图可以看出:
通过序列化的方式花费的时间是:
序列化 开始时间 ==> 15:28:19.095
序列化 结束时间 ==> 15:28:21.848
通过复制域的方式花费的时间是:
复制域 开始时间 ==> 15:28:21.849
复制域 结束时间 ==> 15:28:21.857
使用序列化的方式效率很低