疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java中的浅克隆与深克隆


 

   

  Summary

  浅克隆与深克隆对于JavaSE来说,是个难度系数比较低的概念,但不应该轻视它。

  假设一个场景:对于某个list,代码里并没有任何对其的直接操作,但里面的元素的属性却被改变了,这可能就涉及到这个概念。

  Description

  浅克隆指仅copy对象位于栈内存中的引用(reference)。copy后,新旧两个引用指向同一个堆内存对象(即同一内存区域),但是堆内存中实际的对象copy前后均只有一个。使用"==" operator比较二者的地址会返回true。(不同引用,同一对象)

  深克隆指则会copy一个新的对象并返回相应引用,即开辟了新的堆内存空间,因此使用“==” operator来比较两者的地址时会返回false。(不同引用,不同对象)

  浅克隆(shallow clone)

  clone对象是实例对象时,使用“=”操作符进行浅克隆。

  clone对象是对象数组的元素时,使用 System.arraycoppy() 进行浅克隆。(你非得要用"=" foreach地clone也没人拦着)

  jdk中显式定义的clone操作基本上都使用:

  1 System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

  例如ArrayList中的clone()、Arrays.copyOf()等对具体数组的clone其实底层都是调用该方法。

  复制代码

  1 package com.scv.test.clone;

  2

  3 public class ShallowCloneTest {

  4

  5 public static void main(String[] args) throws Exception {

  6 Zerg z0 = new Zerg();

  7 Zerg z1 = z0;

  8 System.out.println("0. " + (z0 == z1));//"="操作符用于对象的浅克隆

  9

  10 Zerg[] array0 = new Zerg[]{new Zerg(), new Zerg()};

  11 Zerg[] array1 = new Zerg[array0.length];

  12 System.arraycopy(array0, 0, array1, 0, array0.length);//System.arraycopy()用于数组的浅克隆

  13 System.out.println("1. " + (array0 == array1));

  14 for(int i = 0; i < array0.length; i++){

  15 System.out.println("Comparing element of array:" + (array0[i] == array1[i]));

  16 }

  17 }

  18

  19 }

  20

  21 class Zerg{

  22

  23 }

  24

  25 /* Output:

  26 0. true

  27 1. false

  28 Comparing element of array:true

  29 Comparing element of array:true

  30 */

  复制代码

  深克隆(deep clone)

  jdk中并没有显示定义深克隆,或者说并没有直接提供工具类来进行。要让你的自定义类支持深克隆,必须具备两个条件:

  implements Cloneable interface.

  override clone() defined in java.lang.Object.

  如果不实现Cloneable而直接override Object的clone(),则会抛出CloneNotSupportedException。

  复制代码

  1 package com.scv.test.clone;

  2

  3 public class DeepCloneTest {

  4

  5 public static void main(String[] args) throws Exception {

  6 CloneableZerg z0 = new CloneableZerg();

  7 CloneableZerg z1 = z0.clone();

  8

  9 System.out.println("0. " + (z0 == z1));

  10 }

  11

  12 }

  13

  14 class CloneableZerg implements Cloneable{

  15

  16 @Override

  17 public CloneableZerg clone() throws CloneNotSupportedException{

  18 return (CloneableZerg)super.clone();

  19 }

  20 }

  21

  22 /* Output:

  23 0. false

  24 */

  复制代码

  实际上,你可以自定义哪些成员变量(field)允许clone,哪些不允许(有点transient的感觉?)。

  jdk中的实现:ArrayList中的浅克隆与深克隆

  复制代码

  1 package com.scv.test.clone;

  2

  3 import java.util.ArrayList;

  4

  5 public class ArrayListCloneTest {

  6

  7 public static void main(String[] args) throws Exception {

  8 CloneTarget t = new CloneTarget();

  9

  10 ArrayList list0 = new ArrayList(1);

  11 list0.add(t);

  12 ArrayList list1 = (ArrayList) list0.clone();

  13 list0.get(0).setFieldA(20);

  14

  15 System.out.println("0. " + (list0 == list1));

  16 System.out.println("1. " + (list0.get(0) == list1.get(0)));

  17 System.out.println("2. " + list1.get(0).getFieldA());

  18 }

  19

  20 }

  21

  22 class CloneTarget implements Cloneable{

  23

  24 private int fieldA = 10;

  25

  26 @Override

  27 public CloneTarget clone() throws CloneNotSupportedException{

  28 return (CloneTarget)super.clone();

  29 }

  30

  31 public void setFieldA(int a){

  32 fieldA = a;

  33 }

  34

  35 public int getFieldA(){

  36 return fieldA;

  37 }

  38 }

  39

  40 /*

  41 * Output:

  42 * 0. false

  43 * 1. true

  44 * 2. 20

  45 */

  复制代码

  操作说明:

  创建一个ArrayList对象list0

  list0中加入一个对象t

  克隆list0对象为list1

  再修改list0中元素(即t)的属性

  结果说明:

  ArrayList实现了Cloneable接口,arraylist.clone()为深克隆,故list0与list1分别指向不同内存区域。

  ArrayList对象的clone()对于内部数组的元素仅为浅克隆,故list0中的元素(t)与list1中的元素为同一个,对list0元素的修改将影响到list1的元素。