1.深浅copy的定义

  1.浅拷贝:只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制。

  2.深拷贝:对象,对象内部的引用均复制。

1.1浅拷贝图示

  为了更好的理解它们的区别我们假设有一个对象A,它包含有2对象对象A1和对象A2,如图:

  对象A进行浅拷贝后,得到对象B但是对象A1和A2并没有被拷贝,如图:

1.2深拷贝图示

  对象A进行深拷贝,得到对象B的同时A1和A2连同它们的引用也被拷贝,如图:

 

1.3Java的clone()方法

  

  1.clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足: 
(1)对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象 。
(2)对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样 。
(3)如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

  2.Java中对象的克隆 :

(1)为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
(2)在派生类中覆盖基类的clone()方法,并声明为public。
(3)在派生类的clone()方法中,调用super.clone()。
(4)在派生类中实现Cloneable接口。

 2.浅拷贝

2.1浅拷贝分类

  1.对于数据类型是基本数据类型的成员变量:

浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。

  2.对于数据类型是引用数据类型的成员变量:

比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

2.2浅拷贝实现方式

  1.通过拷贝构造方法实现浅拷贝:

拷贝构造方法指的是该类的构造方法参数为该类的对象。使用拷贝构造方法可以很好地完成浅拷贝,直接通过一个现有的对象创建出与该对象属性相同的新的对象。

注意:如果在拷贝构造方法中,对引用数据类型变量逐一开辟新的内存空间,创建新的对象,也可以实现深拷贝。而对于一般的拷贝构造,则一定是浅拷贝。

  2.通过重写clone()方法进行浅拷贝:

Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。但是需要注意:(1)、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。(2)、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。

3.深拷贝

3.1深拷贝的实现方式:

  1.通过重写clone方法来实现深拷贝:

与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝=深拷贝。

  2.通过对象序列化实现深拷贝:

虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。

  • 被复制对象的继承链、引用链上的每一个对象都实现java.io.Serializable接口。这个比较简单,不需要实现任何方法,serialVersionID的要求不强制,对深拷贝来说没毛病。

  • 实现自己的deepClone方法,将this写入流,再读出来。

 1 public Object deepClone() throws Exception
 2     {
 3         // 序列化
 4         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 5         ObjectOutputStream oos = new ObjectOutputStream(bos);
 6 
 7         oos.writeObject(this);
 8 
 9         // 反序列化
10         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
11         ObjectInputStream ois = new ObjectInputStream(bis);
12 
13         return ois.readObject();
14     }

 

 posted on 2019-05-11 17:06  hahahaer  阅读(141)  评论(0编辑  收藏  举报