目录
浅拷贝
浅拷贝是指创建一个新的对象,该对象的内容是原始对象中各项的引用。换句话说,浅拷贝仅复制了原始对象中元素的引用,而不是元素本身的拷贝
class People implements Cloneable{
private String name;
private int age;
private Address address;
public People(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address.toString() +
'}';
}
public Address getAddress() {
return address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Address{
private String city;
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
public Address(String city) {
this.city = city;
}
public void setCity(String city) {
this.city = city;
}
}
public class Clone {
public static void main(String[] args) throws CloneNotSupportedException {
Address address=new Address("杭州");
People people=new People("李飞",22,address);
People clonePeople= (People) people.clone();
System.out.println(people);
System.out.println(clonePeople);
//修改 address 值
address.setCity("北京");
System.out.println(people);
System.out.println(clonePeople);
System.out.println(clonePeople.getAddress().hashCode());
System.out.println(people.getAddress().hashCode());
}
}
运行结果:
原始类 Person 实现 Cloneable 接口,并且覆写 clone 方法,它还有三个属性,一个引用类型 String定义的 name,一个基本类型 int定义的age,还有一个引用类型 Address ,这是一个自定义类,这个类包含 city 属性。
当我修改了原对象的Address中的city属性(从杭州变更为北京),从输出结果来看,克隆对象的city属性也发生了改变,然后又打印了原对象和克隆对象的Address的哈希地址,发现他们还是同一个对象。这就是浅拷贝,对于引用型对象只会复制引用,他们指向的还是同一块堆内存空间,而不是创建一个新的对象。
深拷贝
深拷贝是指创建一个新的对象,并递归地复制原始对象及其所有嵌套对象,从而实现完全独立的拷贝。对原对象的操作不会影响新对象。
实现cloneable接口
那如果实现深拷贝呢,第一种方法就是复制对象里的所有引用对象都实现cloneable接口并重写clone 方法就行,这里的自定义的引用对象就是Address,当我们将该对象也实现cloneable接口,就可以实现深拷贝了
package base;
class People implements Cloneable{
// People其他属性没展示,可参考上面代码
@Override
protected Object clone() throws CloneNotSupportedException {
People people =(People) super.clone();
people.address = (Address) address.clone();
return people;
}
}
class Address implements Cloneable{
private String city;
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
public Address(String city) {
this.city = city;
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Clone {
public static void main(String[] args) throws CloneNotSupportedException {
Address address=new Address("杭州");
People people=new People("李飞",22,address);
People clonePeople= (People) people.clone();
System.out.println(people);
System.out.println(clonePeople);
//修改 address 值
address.setCity("北京");
System.out.println(people);
System.out.println(clonePeople);
System.out.println(clonePeople.getAddress().hashCode());
System.out.println(people.getAddress().hashCode());
}
}
执行结果:
上面主要修改点一个就是Address也实现了 cloneable接口,重写了clone 方法,然后People中clone方法进行了修改,调用了Address的clone方法。
但是这种做法有个弊端,这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。
序列化
序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。
注意每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。
public Object deepClone() throws Exception{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
完整代码:
class People implements Serializable {
private String name;
private int age;
private Address address;
public People(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address.toString() +
'}';
}
public Address getAddress() {
return address;
}
public Object deepClone() throws Exception{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
class Address implements Serializable{
private String city;
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
public Address(String city) {
this.city = city;
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Clone {
public static void main(String[] args) throws Exception {
Address address=new Address("杭州");
People people=new People("李飞",22,address);
People clonePeople= (People) people.deepClone();
System.out.println(people);
System.out.println(clonePeople);
//修改 address 值
address.setCity("北京");
System.out.println(people);
System.out.println(clonePeople);
System.out.println(clonePeople.getAddress().hashCode());
System.out.println(people.getAddress().hashCode());
}
}
运行结果: