Java 克隆
Java 有两种克隆,浅克隆和深克隆
浅克隆
负责拷贝当前对象,但是对象里的对象没法克隆,还是引用该对象的对象变量。
深克隆
负责拷贝当前对象,并且对象的对象也被克隆,既不再引用对象的对象,而是一个新的对象的对象。
浅克隆
使用方法,克隆类实现Cloneable接口,然后重写祖宗类Object的clone()方法;
实现cloneable接口的方法是因为:Cloneable类似于虚拟机的一个表示接口,必须得以实现,才能进行clone,l类似的接口还有Serializable接口。
**重写clone()方法:**将clone()方法的权限扩大为public,这样在其他的类中就可以直接试用clone()方法。
学生类,在这里除了基本变量外,还引用了一个类变量 address;
@SuppressWarnings("serial")
class Student implements Cloneable{ //实现cloneable接口
private int age;
private String name;
private Address address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public IStudent clone() throws CloneNotSupportedException { //重写clone方法
return (IStudent) super.clone();
}
}
地址类
@SuppressWarnings("serial")
public class Address{
private int code;
private String country;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
开始拷贝
public class ShallowCopy {
public static void main(String[] args) throws CloneNotSupportedException {
//生成一个新的地址对象
Address address = new Address();
address.setCode(86);
address.setCountry("CN");
//将该地址赋值给学生1
Student stu = new Student();
stu.setAddress(address);
//学生2克隆学生一
IStudent stud = stu.clone(); //clone是浅拷贝,当对象中的对象改变时,会影响clone的对象
//改变学生1的地址
stu.getAddress().setCode(01);
stu.getAddress().address.setCountry("US");
//输出学生2的地址,实际上学生2的地址没有变
System.out.println(stu.getAddress().getCountry()+ ":" +stud.getAddress().getCountry());
}
}
输出结果
US : 01
意味着,当改变学生1的地址的同时会改变学生2的地址,因为浅拷贝只能克隆表层对象,而address是深层对象,但是基本变量的age 和 final修饰的String不受影响。
结论
如果拷贝的对象的属性都是基本变量或者是单一层次的对象,则可以使用浅克隆,如果有深层对象,例如Address则不能使用浅克隆,只能使用深克隆。
深拷贝
两种,一种是在多层浅拷贝对象中的对象,另一种就是通过反序列化实现深克隆
普通深克隆
也就是多层浅克隆
class Students implements Cloneable{ //实现cloneable接口,浅克隆条件一
private int age;
private String name;
private IAddress address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IAddress getAddress() {
return address;
}
public void setAddress(IAddress address) {
this.address = address;
}
public Students clone() throws CloneNotSupportedException { //重写object的clone()方法,浅克隆的条件2
return (Students) super.clone();
}
}
为了实现深克隆,所以,Iadress类也需要实现深克隆
public class IAddress implements Cloneable{ //同样先实现cloneable接口
private int code;
private String country;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public IAddress clone() throws CloneNotSupportedException { //然后重写object的object()方法
return (IAddress) super.clone();
}
}
执行深克隆
public class DeepCopy {
public static void main(String[] args) throws CloneNotSupportedException {
Students stu = new Students();
IAddress address = new IAddress();
address.setCode(86);
address.setCountry("CN");
stu.setAddress(address);
Students stud = stu.clone();
stud.setAddress(address.clone()); //同时需要对其子对象进行拷贝,在复杂的对象包含关系中不适用
address.setCode(01);
address.setCountry("US");
System.out.println(stud.getAddress().getCountry() + ":" + stud.getAddress().getCode());
}
}
输出结果
01:US //两个对象完全克隆,对象中的对象,互相不受影响。
实现序列化完成深克隆拷贝(推荐)
通过将对象序列化,之后反序列化回来生成新的对象。
步骤:
- 实现serializalble接口
- 通过序列化和反序列化生成新的对象
IStudent类实现了cloneable接口和Serializable接口
@SuppressWarnings("serial")
class IStudent implements Serializable{
private int age;
private String name;
private Address address;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
通过序列化进行深clone的拷贝
public class SerializationDeepCopy {
public static void main(String[] args) {
IStudent stu = new IStudent();
Address address = new Address();
address.setCode(86);
address.setCountry("CN");
stu.setAddress(address);
IStudent stud = deepCopy(stu);
address.setCode(01);
address.setCountry("US");
System.out.println(stud.getAddress().getCountry() + ":" + stu.getAddress().getCountry());
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepCopy(IStudent stu) { //进行序列化和反序列化操作
IStudent student = null;
try
{
ByteArrayOutputStream bios = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bios);
os.writeObject(stu);
ByteArrayInputStream ios = new ByteArrayInputStream(bios.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
student = (IStudent) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return (T) student;
}
}
输出结果
01:US //完全克隆原来的对象
也可以直接调用org.apache.commons.lang3.SerializationUtils中的方法来进行生克隆
在子类中加上
class StudentI implements Cloneable,Serializable{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return SerializationUtils.clone(this); //将该方法直接重写到clone()方法
}