欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

探討java深拷貝

 更新時(shí)間:2016年02月14日 14:19:53   作者:楚巖  
這篇文章主要針對(duì)java深拷貝的相關(guān)內(nèi)容進(jìn)行解析,幫助大家學(xué)習(xí)理解java深拷貝,感興趣的小伙伴們可以參考一下

本文將討論以下4個(gè)問(wèn)題

    1. java Cloneable接口實(shí)現(xiàn)深拷貝
    2. java 序列化實(shí)現(xiàn)深拷貝
    3. 號(hào)稱(chēng)最快的深拷貝二方庫(kù)cloning源碼分析
    4. 幾種拷貝方式速度的比較

深拷貝的概念本文就不說(shuō)了。在C++中實(shí)現(xiàn)深拷貝一般情況下重載賦值操作符 “=” 來(lái)實(shí)現(xiàn)同一個(gè)類(lèi)的對(duì)象間的深拷貝,所以很自然的在java中我們也同樣可以定義一個(gè)copy函數(shù),在函數(shù)內(nèi)部為對(duì)象的每一個(gè)屬性作賦值操作。這種方式簡(jiǎn)單自然,但存在一個(gè)致命性的問(wèn)題:如果有一天在類(lèi)中新增加了一個(gè)需要深拷貝的屬性,那么相應(yīng)的copy函數(shù)也得進(jìn)行修改,這種方法給類(lèi)的可擴(kuò)展性帶來(lái)了極大的不方便。怎么解決這種問(wèn)題,且看接下來(lái)的1、2、3章節(jié)的實(shí)現(xiàn)方式和4節(jié)的速度測(cè)試。
1. java Cloneable接口實(shí)現(xiàn)深拷貝
這種方式,需要類(lèi)實(shí)現(xiàn)Colneable接口 clone 函數(shù),在clone函數(shù)中調(diào)用super.clone。這種方式的深拷貝同樣會(huì)帶來(lái)另一個(gè)問(wèn)題,如果類(lèi)中有其他類(lèi)的對(duì)象作為屬性,則其他的類(lèi)也需要重載并實(shí)現(xiàn)Cloneable接口。來(lái)一個(gè)例子,在下例中ComplexDO中包含了SimpleDO對(duì)象,要實(shí)現(xiàn)ComplexDO深拷貝,則需要先實(shí)現(xiàn)SimpleDO的clone接口:

public class SimpleDO implements Cloneable, Serializable {
    private int x = 1;
    private String s = "simpleDO";

    @Override
    protected Object clone() throws CloneNotSupportedException {
      SimpleDO newClass = (SimpleDO)super.clone();
      return newClass;
    }
  }

  public class ComplexDO implements Cloneable, Serializable {
    private int x = 1;
    private String s = "complex";
    private Integer a = 123;
    private Integer b = 1234;
    private Integer c = 1334455;
    private String s2 = "hehehe";
    private String s3 = "hahahaha";
    private Long id = 1233245L;
    private ArrayList<SimpleDO> l = new ArrayList<SimpleDO>();

    @Override
    public Object clone() throws CloneNotSupportedException {
      ComplexDO newClass = (ComplexDO) super.clone();
      newClass.l = new ArrayList<SimpleDO>();
      for (SimpleDO simple : this.l) {
        newClass.l.add((SimpleDO) simple.clone());
      }
      return newClass;
    }
  }

需要注意的是很多文章說(shuō)String類(lèi)型的對(duì)象賦值操作符是深拷貝,但是其實(shí)在java中使用賦值操作符的都屬于淺拷貝,但為什么這么明顯的錯(cuò)誤這么多的文章會(huì)非要說(shuō)這個(gè)是深拷貝呢?我的理解是String、類(lèi)型的屬性都是基本類(lèi)型,而且提供的方法只要是設(shè)計(jì)到內(nèi)部數(shù)據(jù)的變動(dòng)都會(huì)new一個(gè)新的對(duì)象出來(lái)。所以一個(gè)String的操作不會(huì)影響到其原先指向的內(nèi)存。所以一般說(shuō)String等基礎(chǔ)類(lèi)的賦值操作為深拷貝。
由于這個(gè)原因,在使用String字符串拼接的時(shí)候,需要開(kāi)辟新的內(nèi)存,所以很多人建議用StringBuilder來(lái)代替String來(lái)做拼接,因?yàn)镾tringBuilder只有在內(nèi)置的char數(shù)組范圍不夠的時(shí)候才重新申請(qǐng)更大的內(nèi)存(對(duì)于現(xiàn)代JVM,會(huì)對(duì)代碼調(diào)優(yōu),String+String會(huì)被優(yōu)化成StringBuilder.append的相類(lèi)似的指令)。與拼接相對(duì)的裁剪,在String有個(gè)subString函數(shù),當(dāng)使用subString函數(shù)時(shí),新String的內(nèi)部char數(shù)組和原String是否相同?這個(gè)比較有意思,感興趣的可以對(duì)比看看JDK1.6和JKD1.7的實(shí)現(xiàn)。
2. java 序列化實(shí)現(xiàn)深拷貝
這種方式的原理是利用java序列化,將一個(gè)對(duì)象序列化成二進(jìn)制字節(jié)流,然后對(duì)該字節(jié)流反序列化賦值給一個(gè)對(duì)象。代碼示例:

  public Object seirCopy(Object src) {
    try {
      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(byteOut);
      out.writeObject(src);

      ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
      ObjectInputStream in = new ObjectInputStream(byteIn);
      Object dest = in.readObject();
      return dest;
    } catch (Exception e) {
      //do some error handler
      return null;
    }
 }

當(dāng)然,也可以選用json等序列化的庫(kù)來(lái)完成序列化,這種方式有效的規(guī)避了Cloneabel接口的可擴(kuò)展缺點(diǎn),一個(gè)函數(shù)就可以基本上適用于所有的類(lèi).缺點(diǎn)是相對(duì)內(nèi)存拷貝,序列化需要先將對(duì)象轉(zhuǎn)換成二進(jìn)制字節(jié)流,然后反序列化將該二進(jìn)制字節(jié)流重新拷貝到一塊對(duì)象內(nèi)存,相對(duì)慢點(diǎn)。
3. 號(hào)稱(chēng)最快的深拷貝二方庫(kù)cloning源碼分析
在源碼中,核心的處理邏輯在Cloner類(lèi)中,
分兩條遞歸鏈路:

  • (1)deepClone->cloneInternal->fastClone->cloneInternal
  • (2)deepClone->cloneInternal->cloneObject->cloneInternal

在(1)中fastClone完成的是繼承自IfastCloner接口類(lèi)的對(duì)象,即都是些集合操作的拷貝;
在(2)中cloneObject完成的是通過(guò)反射機(jī)制拿到普通對(duì)象的每一個(gè)屬性,然后對(duì)使用Objenesis新生成對(duì)象的屬性賦值。
這種方式可擴(kuò)展性強(qiáng),不僅可以依靠其現(xiàn)有的代碼完成深拷貝,還可以自己定義一些克隆的方式和不需要克隆的類(lèi)型,靈活性強(qiáng)。
4. 幾種拷貝方式速度的比較
上述3中模式都可以完成深拷貝,那種拷貝的方式速度最快是我們所關(guān)心的。
先上測(cè)試代碼:
 

  public void testCloneComplex() throws CloneNotSupportedException {
    final int copyCount = 1;
    List<ComplexDO> complexDOList = new ArrayList<ComplexDO>(copyCount * 3);
    final ComplexDO complex = new ComplexDO();

    //調(diào)用二方庫(kù)
    long start = System.currentTimeMillis();
    for(int i = 0; i < copyCount; ++i) {
      final ComplexDO deepClone = cloner.deepClone(complex);
      complexDOList.add(deepClone);
    }
    long end = System.currentTimeMillis();
    System.out.println("deepClone cost time=" + (end-start));

    //調(diào)用Cloneable接口實(shí)現(xiàn)的clone函數(shù)
    start = System.currentTimeMillis();
    for(int i = 0; i < copyCount; ++i) {
      final ComplexDO interfaceClone = (ComplexDO) complex.clone();
      complexDOList.add(interfaceClone);
    }
    end = System.currentTimeMillis();
    System.out.println("interfaceClone cost time=" + (end-start));

    //序列化與反序列化生成新對(duì)象
    start = System.currentTimeMillis();
    for(int i = 0; i < copyCount; ++i) {
      final ComplexDO seirClone = seirCopy(complex);
      complexDOList.add(seirClone);
    }
    end = System.currentTimeMillis();
    System.out.println("seirClone cost time=" + (end-start));
  }


運(yùn)行結(jié)果的單位為毫秒(此數(shù)據(jù)忽略不計(jì)算java熱點(diǎn)和可能的gc)。

從這個(gè)表可以得出結(jié)論:

1、實(shí)現(xiàn)Cloneable接口的拷貝是最快的,因?yàn)樗簧婕暗搅藘?nèi)存拷貝,但是如果涉及的屬性為普通對(duì)象比較多的時(shí)候?qū)懫饋?lái)麻煩點(diǎn)
2、序列化/反序列化拷貝最慢
3、使用cloning庫(kù),由于使用了遞歸和反射機(jī)制相對(duì)Cloneable接口實(shí)現(xiàn)的拷貝要慢,但比序列化方式要快。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • java 判斷l(xiāng)ist是否為空過(guò)程解析

    java 判斷l(xiāng)ist是否為空過(guò)程解析

    這篇文章主要介紹了java 判斷l(xiāng)ist是否為空過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Java C++實(shí)現(xiàn)相同MD5加密算法的方式

    Java C++實(shí)現(xiàn)相同MD5加密算法的方式

    這篇文章主要介紹了Java與C++實(shí)現(xiàn)相同MD5加密算法的方法,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • Java通過(guò)Fork/Join優(yōu)化并行計(jì)算

    Java通過(guò)Fork/Join優(yōu)化并行計(jì)算

    這篇文章主要為大家詳細(xì)介紹了Java通過(guò)Fork、Join來(lái)優(yōu)化并行計(jì)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Java實(shí)現(xiàn)HashMap排序方法的示例詳解

    Java實(shí)現(xiàn)HashMap排序方法的示例詳解

    這篇文章主要通過(guò)一些示例為大家介紹了Java對(duì)HashMap進(jìn)行排序的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解一下
    2022-05-05
  • Java8中Optional類(lèi)型和Kotlin中可空類(lèi)型的使用對(duì)比

    Java8中Optional類(lèi)型和Kotlin中可空類(lèi)型的使用對(duì)比

    這篇文章主要給大家介紹了關(guān)于Java8中Optional類(lèi)型和Kotlin中可空類(lèi)型的使用對(duì)比,文中通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • Spring?Boot快速過(guò)濾出一次請(qǐng)求的所有日志

    Spring?Boot快速過(guò)濾出一次請(qǐng)求的所有日志

    這篇文章主要介紹了Spring?Boot快速過(guò)濾出一次請(qǐng)求的所有日志,本文講述了如何使用MDC工具來(lái)快速過(guò)濾一次請(qǐng)求的所有日志,并通過(guò)裝飾器模式使得MDC工具在異步線(xiàn)程里也能生效,需要的朋友可以參考下
    2022-11-11
  • springboot 傳參校驗(yàn)@Valid及對(duì)其的異常捕獲方式

    springboot 傳參校驗(yàn)@Valid及對(duì)其的異常捕獲方式

    這篇文章主要介紹了springboot 傳參校驗(yàn)@Valid及對(duì)其的異常捕獲方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Spring使用注解存儲(chǔ)Bean對(duì)象的方法詳解

    Spring使用注解存儲(chǔ)Bean對(duì)象的方法詳解

    在使用學(xué)習(xí)使用 Spring過(guò)程中,當(dāng)我們要實(shí)現(xiàn)一個(gè)功能的時(shí)候,先應(yīng)該考慮的是有沒(méi)有相應(yīng)的注解是實(shí)現(xiàn)對(duì)應(yīng)功能的,Spring 中很多功能的配置都是可以依靠注解實(shí)現(xiàn)的,而本篇中介紹的是使用注解來(lái)存儲(chǔ) Bean 對(duì)象
    2023-07-07
  • 修改maven項(xiàng)目端口號(hào)的方法

    修改maven項(xiàng)目端口號(hào)的方法

    今天小編就為大家分享一篇修改maven項(xiàng)目端口號(hào)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • RocketMQ-延遲消息的處理流程介紹

    RocketMQ-延遲消息的處理流程介紹

    這篇文章主要介紹了RocketMQ-延遲消息的處理流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論