对于深克隆而言,如果类有许多引用类型的域,那么重写clone方法依次复制各个域是非常麻烦的。如果引用类型的域也是由引用类型组成的,则应该考虑使用序列化的方法实现深克隆。
序列化可以将任意对象写入到流中。根据流的类型不同,可以将对象写入到文件中。也可以将对象写入到字节数组中。克隆对象时一般不需要先进行保存,因此将使用字节数组。在写入完成后,再将其读出就可以实现克隆。使用序列化可以不用考虑引用类型的域,编写clone()方法相对简单,但是要求引用类型也实现Serializable接口。
注:如果使用了API中的类并且该类并没有实现Serializable接口,则该域需要使用transient修饰。
代码如下:
package com.trouble.clone;
import java.io.Serializable;
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String state;
private String province;
private String city;
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuilder sb = new StringBuilder();
sb.append("国家:" + state + ",");
sb.append("省:" + province + ",");
sb.append("市:" + city);
return sb.toString();
}
}
package com.trouble.clone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Employee implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private Address address;
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
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;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ",");
sb.append("年龄:" + age + "\n");
sb.append("地址:" + address);
return sb.toString();
}
@Override
protected Employee clone() {
Employee employee = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
try {
ObjectInputStream ois = new ObjectInputStream(bais);
employee = (Employee) ois.readObject();
ois.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return employee;
}
}
package com.trouble.clone;
public class Test {
public static void main(String[] args){
Address address = new Address("中国","吉林","长春");
Employee employee1 = new Employee("明月科技",12,address);
System.out.println(employee1);
Employee employee2 = employee1.clone();
System.out.println(employee2);
employee2.setAge(14);
employee2.getAddress().setProvince("四川");
employee2.getAddress().setCity("成都");
System.out.println(employee1);
System.out.println(employee2);
}
}
注:首先,对于任何一个序列化的对象,都要求其实现Serializable接口。其次,如果该类的域中有引用类型,则要求该引用类型也实现Serializable接口,以此类推,最后序列化方式实现克隆会比直接克隆各个引用类型域慢,这一点在效率优先时要考虑。