一.对象拷贝
对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部 数据。Java中有三种类型的对象拷贝:
- 浅拷贝(Shallow Copy)
- 深拷贝(Deep Copy)
- 延迟拷贝(Lazy Copy)
1. 浅拷贝 Shallow Copy
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
代码示例:
public class Subject {
private String name;
public Subject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Student implements Cloneable {
//引用类型
private Subject subject;
//基本类型
private String name;
public Student(String name, String subjectName) {
this.name = name;
subject = new Subject(subjectName);
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
try {
// 直接调用父类的clone()方法
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
public class ShallowCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
/*原始对象*/
System.out.println("==============Source================");
Student sourceStu = new Student("stuName", "subjectName");
System.out.println("Source对象的Name: " + sourceStu.getName());
System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());
/*拷贝对象*/
System.out.println("================Clone===============");
Student cloneStu = (Student) sourceStu.clone();
System.out.println("Clone对象的Name: " + cloneStu.getName());
System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
/*修改克隆对象的基本数据类型变量 源对象的该变量不会发生改变*/
System.out.println("================Modify===============");
cloneStu.setName("cloneName");
System.out.println("Clone对象的Name: " + cloneStu.getName());
System.out.println("Source对象的Name: " + sourceStu.getName());
/*修改克隆对象的引用类型变量 源对象的该变量会发生改变*/
cloneStu.getSubject().setName("cloneSubjectName");
System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());
}
}
Student类里有一个基本数据类型的name和引用数据类型的subject
当使用浅克隆时,修改克隆对象的基本数据类型变量,源对象的该变量不会发生改变 而引用数据类型变量会随之改变
输出结果:
==============Source================
Disconnected from the target VM, address: '127.0.0.1:64518', transport: 'socket'
Source对象的Name: stuName
Source对象的Subject的Name: subjectName
================Clone===============
Clone对象的Name: stuName
Clone对象的Subject的Name: subjectName
================Modify===============
Clone对象的Name: cloneName
Source对象的Name: stuName
Clone对象的Subject的Name: cloneSubjectName
Source对象的Subject的Name: cloneSubjectName
二. 深拷贝 Deep Copy
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
代码示例:
Subject类和Student类和上例相同 仅仅修改了Student类内的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
/*深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立*/
return new Student(getName(), getSubject().getName());
}
public class DeepCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
/*原始对象*/
System.out.println("==============Source================");
Student sourceStu = new Student("stuName", "subjectName");
System.out.println("Source对象的Name: " + sourceStu.getName());
System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());
/*拷贝对象*/
System.out.println("================Clone===============");
Student cloneStu = (Student) sourceStu.clone();
System.out.println("Clone对象的Name: " + cloneStu.getName());
System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
/*修改克隆对象的基本数据类型变量 源对象的该变量不会发生改变*/
System.out.println("================Modify===============");
cloneStu.setName("cloneName");
System.out.println("Clone对象的Name: " + cloneStu.getName());
System.out.println("Source对象的Name: " + sourceStu.getName());
/*修改克隆对象的引用类型变量 源对象的该变量会发生改变*/
cloneStu.getSubject().setName("cloneSubjectName");
System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());
}
}
输出结果
==============Source================
Disconnected from the target VM, address: '127.0.0.1:64577', transport: 'socket'
Source对象的Name: stuName
Source对象的Subject的Name: subjectName
================Clone===============
Clone对象的Name: stuName
Clone对象的Subject的Name: subjectName
================Modify===============
Clone对象的Name: cloneName
Source对象的Name: stuName
Clone对象的Subject的Name: cloneSubjectName
Source对象的Subject的Name: subjectName
使用序列化进行深拷贝
代码示例:
public class Circle implements Serializable {
private int x;
private int y;
public Circle(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "Circle{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class SerializableDeepCopyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
Circle sourceCircle = new Circle(400, 300);
/*写入Object*/
ByteArrayOutputStream bao = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bao);
oos.writeObject(sourceCircle);
oos.flush();
/*读取Object*/
ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
ois = new ObjectInputStream(bai);
Circle cloneCircle = (Circle) ois.readObject();
System.out.println("sourceCircle == cloneCircle:" + (sourceCircle == cloneCircle));
cloneCircle.setX(1000);
cloneCircle.setY(2000);
System.out.println("sourceCircle" + sourceCircle);
System.out.println("cloneCircle" + cloneCircle);
}
}
输出结果:
sourceCircle == cloneCircle:false
sourceCircleCircle{x=400, y=300}
cloneCircleCircle{x=1000, y=2000}
三. 延迟拷贝
延迟拷贝是浅拷贝和深拷贝的一个组合,实际上很少会使用。 当最开始拷贝一个对象时,会使用速度较快的浅拷贝,还会使用一个计数器来记录有多少对象共享这个数据。当程序想要修改原始的对象时,它会决定数据是否被共享(通过检查计数器)并根据需要进行深拷贝。
延迟拷贝从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度。当原始对象中的引用不经常改变的时候可以使用延迟拷贝。由于存在计数器,效率下降很高,但只是常量级的开销。而且, 在某些情况下, 循环引用会导致一些问题。