zoukankan      html  css  js  c++  java
  • Java:浅克隆(shallow clone)与深克隆(deep clone)

    Summary

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

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

    Description

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

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

    浅克隆(shallow clone)

    1. clone对象是实例对象时,使用“=”操作符进行浅克隆。
    2. 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 */
    验证Shallow Clone

     

    深克隆(deep clone)

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

    1. implements Cloneable interface.
    2. 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 */
    验证Deep Clone

    实际上,你可以自定义哪些成员变量(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<CloneTarget> list0 = new ArrayList<CloneTarget>(1);
    11         list0.add(t);
    12         ArrayList<CloneTarget> list1 = (ArrayList<CloneTarget>) 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 */
    Click Me

    操作说明:

    1. 创建一个ArrayList对象list0
    2. list0中加入一个对象t
    3. 克隆list0对象为list1
    4. 再修改list0中元素(即t)的属性

    结果说明:

    1. ArrayList实现了Cloneable接口,arraylist.clone()为深克隆,故list0与list1分别指向不同内存区域。
    2. ArrayList对象的clone()对于内部数组的元素仅为浅克隆,故list0中的元素(t)与list1中的元素为同一个,对list0元素的修改将影响到list1的元素。
  • 相关阅读:
    for是个什么玩意
    面向对象 多态
    面向对象 接口
    面向对象oop
    面向对象基本
    java修饰符的作用范围
    static 关键字介绍
    JsonLayout log4j2 json格式输出日志
    多线程--Thread.join方法
    idea常用实用快捷键
  • 原文地址:https://www.cnblogs.com/lawrencechen/p/4232619.html
Copyright © 2011-2022 走看看