一、概述
对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。:
浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。
二、浅拷贝
public class TestDemo{
public static void main(String
[] args
) {
Friend friend
= new Friend("Ace","man",18);
Person person
= new Person("Jeffrey",18, friend
);
System
.out
.println("原始对象: " + person
);
Person clonedPperson
= (Person
)person
.clone();
System
.out
.println("拷贝对象: " + clonedPperson
);
System
.out
.println("原始对象和拷贝对象是否一样: " + (person
== clonedPperson
));
person
.setAge(20);
System
.out
.println("修改基本类型后的对比:");
System
.out
.println("原始对象: " + person
);
System
.out
.println("拷贝对象: " + clonedPperson
);
person
.getFriend().setAge(30);
System
.out
.println("修改引用类型后的对比:");
System
.out
.println("原始对象: " + person
);
System
.out
.println("拷贝对象: " + clonedPperson
);
}
}
class Person implements Cloneable{
private String name
;
private int age
;
private Friend friend
;
public Person(String name
, int age
,Friend friend
) {
this.name
= name
;
this.age
= age
;
this.friend
= friend
;
}
public void setName(String name
) {
this.name
= name
;
}
public void setAge(int age
) {
this.age
= age
;
}
public Friend
getFriend() {
return friend
;
}
@Override
public Object
clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e
) {
return null
;
}
}
public String
toString() {
return "Person={ " + "name=" + name
+ ", age=" + age
+ ", friend=" + friend
+" }";
}
}
class Friend{
private String name
;
private String sex
;
private int age
;
public Friend(String name
,String sex
,int age
) {
this.name
= name
;
this.sex
= sex
;
this.age
= age
;
}
public void setSex(String sex
) {
this.sex
= sex
;
}
public void setAge(int age
) {
this.age
= age
;
}
public String
toString() {
return "{ Friend:" + "name=" + name
+ ", sex=" + sex
+ ", age=" + age
+ " }";
}
}
输出结果:
原始对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 原始对象和拷贝对象是否一样: false 修改基本类型后的对比: 原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=18 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } } 修改引用类型后的对比: 原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=30 } } 拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=30 } }
根据结果来看,通过Clonable接口并重写Object类的clone()方法来赋值的对象,值类型的赋值不会改变,引用类型会改变,印证了只是赋值栈上面的数据和引用对象地址,最终指向堆的内存地址一致。
三、深拷贝
常用的方案有两种:
继续利用 clone()方法,对其内的引用类型的变量,再进行一次clone()(Friend实现),或者在clone()创建新的引用变量赋值和新的对象序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象。
1、clone
修改Pesron类,新创建一个对象返回:
@Override
public Object
clone(){
return new Person(name
, age
,new Friend(friend
.name
,friend
.sex
,friend
.age
));
}
2、序列化实现
具体参考Java 序列化,只是把FileOutputStream改成ByteArrayOutputStream。因为主要是FileOutputStream作用于文件,后者作用于byte[]。
参考Java 深拷贝和浅拷贝和细说 Java 的深拷贝和浅拷贝