深拷贝和浅拷贝
Java 中的对象拷贝主要分为两类:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。
浅拷贝
1.定义
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将这个属性值复制一份给新的对象,因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据;对于数据类型是引用数据类型的成员变量,比如说该成员变量是某个数组或某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(即内存地址)复制一份给新的对象,也就说这两个不同对象的该成员变量都指向了同一个地址,此时在一个对象中修改该成员变量就会影响到另一个对象所拷贝得到的数据。
2.实例
父对象类ParentPrototype
public class ParentPrototype implements Cloneable {
private String id;
private String name;
public ParentPrototype() {}
public ParentPrototype(String id, String name) {
this.id = id;
this.name = name;
}
// get。。。 set。。。
@Override
protected ParentPrototype clone() throws CloneNotSupportedException {
System.out.println("具体的原型对象复制成功");
return (ParentPrototype) super.clone();
}
}
原型对象类Prototype
public class Prototype implements Cloneable {
private String id;
private String name;
private ParentPrototype parentPrototype;
public Prototype() { }
public Prototype(String id, String name) {
this.id = id;
this.name = name;
}
// get。。。 set。。。
/**
* 对象的类必须实现接口Cloneable,否则抛出CloneNotSupportedException。
* Object.clone()默认的拷贝方法,为浅拷贝。
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Prototype clone() throws CloneNotSupportedException {
System.out.println("具体的原型对象复制成功");
return (Prototype) super.clone();
}
}
测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype p = new Prototype("001","lucy");
p.setParentPrototype(new ParentPrototype("p001","张三"));
Prototype p2 = p.clone();
p2.getParentPrototype().setName("李四");
System.out.println(p);
System.out.println(p2);
System.out.println(p.getParentPrototype());
System.out.println(p2.getParentPrototype());
System.out.println(p == p2);
System.out.println(p.getParentPrototype() == p2.getParentPrototype());
System.out.println(p.getParentPrototype().getName());
System.out.println(p2.getParentPrototype().getName());
/**
输出:
com.joiwsev.patterns.prototype.before.Prototype@3d075dc0
com.joiwsev.patterns.prototype.before.Prototype@214c265e
com.joiwsev.patterns.prototype.before.ParentPrototype@448139f0
com.joiwsev.patterns.prototype.before.ParentPrototype@448139f0
false
true
李四
李四
结果分析:两个对象Prototype p和p2分别指向不同的对象,但是两个对象p和p1中所引用对象的
两个ParentPrototype依然指向同一个对象。说明为浅拷贝
*/
}
}
结果分析:两个对象Prototype p和p2分别指向不同的对象,但是两个对象p和p1中所引用对象的两个ParentPrototype依然指向同一个对象。说明为浅拷贝。
深拷贝
1.定义
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容。
深拷贝有以下几个特点:
- 对于基本数据类型的成员对象,直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
- 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响(和浅拷贝不同)。
- 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
- 深拷贝相比于浅拷贝速度较慢并且花销较大。
2.实例
更改Prototype
类中的clone()方法
public class Prototype implements Cloneable {
/**
* 对象的类必须实现接口Cloneable,否则抛出CloneNotSupportedException。
* Object.clone()默认的拷贝方法,为浅拷贝。
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Prototype clone() throws CloneNotSupportedException {
System.out.println("具体的原型对象复制成功");
Prototype prototype = (Prototype) super.clone();
ParentPrototype parentPrototype = prototype.getParentPrototype().clone();
prototype.setParentPrototype(parentPrototype);
return (Prototype) prototype;
}
}
测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype p = new Prototype("001","lucy");
p.setParentPrototype(new ParentPrototype("p001","张三"));
Prototype p2 = p.clone();
p2.getParentPrototype().setName("李四");
System.out.println(p);
System.out.println(p2);
System.out.println(p.getParentPrototype());
System.out.println(p2.getParentPrototype());
System.out.println(p == p2);
System.out.println(p.getParentPrototype() == p2.getParentPrototype());
System.out.println(p.getParentPrototype().getName());
System.out.println(p2.getParentPrototype().getName());
/**
输出:
com.joiwsev.patterns.prototype.before.Prototype@3d075dc0
com.joiwsev.patterns.prototype.before.Prototype@214c265e
com.joiwsev.patterns.prototype.before.ParentPrototype@448139f0
com.joiwsev.patterns.prototype.before.ParentPrototype@7cca494b
false
false
张三
李四
结果分析:两个对象Prototype p和p2分别指向不同的对象,但是两个对象p和p1中所引用对象的
两个ParentPrototype指向两个对象。说明为深拷贝
*/
}
}
结果分析:两个对象Prototype p和p2分别指向不同的对象,但是两个对象p和p1中所引用对象的两个ParentPrototype指向两个对象。说明为深拷贝。