程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 商量java深拷貝

商量java深拷貝

編輯:關於JAVA

商量java深拷貝。本站提示廣大學習愛好者:(商量java深拷貝)文章只能為提供參考,不一定能成為您想要的結果。以下是商量java深拷貝正文


本文將評論辯論以下4個成績

    1. java Cloneable接話柄現深拷貝
    2. java 序列化完成深拷貝
    3. 號稱最快的深拷貝二方庫cloning源碼剖析
    4. 幾種拷貝方法速度的比擬

深拷貝的概念本文就不說了。在C++中完成深拷貝普通情形下重載賦值操作符 “=” 來完成統一個類的對象間的深拷貝,所以很天然的在java中我們也異樣可以界說一個copy函數,在函數外部為對象的每個屬性作賦值操作。這類方法簡略天然,但存在一個致命性的成績:假如有一天在類中新增長了一個須要深拷貝的屬性,那末響應的copy函數也得停止修正,這類辦法給類的可擴大性帶來了極年夜的不便利。怎樣處理這類成績,且看接上去的1、2、3章節的完成方法和4節的速度測試。
1. java Cloneable接話柄現深拷貝
這類方法,須要類完成Colneable接口 clone 函數,在clone函數中挪用super.clone。這類方法的深拷貝異樣會帶來另外一個成績,假如類中有其他類的對象作為屬性,則其他的類也須要重載並完成Cloneable接口。來一個例子,鄙人例中ComplexDO中包括了SimpleDO對象,要完成ComplexDO深拷貝,則須要先完成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;
    }
  }

須要留意的是許多文章說String類型的對象賦值操作符是深拷貝,然則其其實java中應用賦值操作符的都屬於淺拷貝,但為何這麼顯著的毛病這麼多的文章會非要說這個是深拷貝呢?我的懂得是String、類型的屬性都是根本類型,並且供給的辦法只需是設計到外部數據的更改都邑new一個新的對象出來。所以一個String的操作不會影響到其本來指向的內存。所以普通說String等基本類的賦值操作為深拷貝。
因為這個緣由,在應用String字符串拼接的時刻,須要開拓新的內存,所以許多人建議用StringBuilder來取代String來做拼接,由於StringBuilder只要在內置的char數組規模不敷的時刻才從新請求更年夜的內存(關於古代JVM,會對代碼調優,String+String會被優化成StringBuilder.append的相相似的指令)。與拼接絕對的裁剪,在String有個subString函數,當應用subString函數時,新String的外部char數組和原String能否雷同?這個比擬成心思,感興致的可以比較看看JDK1.6和JKD1.7的完成。
2. java 序列化完成深拷貝
這類方法的道理是應用java序列化,將一個對象序列化成二進制字撙節,然後對該字撙節反序列化賦值給一個對象。代碼示例:

  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;
    }
 }

固然,也能夠選用json等序列化的庫來完成序列化,這類方法有用的躲避了Cloneabel接口的可擴大缺陷,一個函數便可以根本上實用於一切的類.缺陷是絕對內存拷貝,序列化須要先將對象轉換成二進制字撙節,然後反序列化將該二進制字撙節從新拷貝到一塊對象內存,絕對慢點。
3. 號稱最快的深拷貝二方庫cloning源碼剖析
在源碼中,焦點的處置邏輯在Cloner類中,
分兩條遞歸鏈路:

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

在(1)中fastClone完成的是繼續自IfastCloner接口類的對象,即都是些聚集操作的拷貝;
在(2)中cloneObject完成的是經由過程反射機制拿到通俗對象的每個屬性,然後對應用Objenesis重生成對象的屬性賦值。
這類方法可擴大性強,不只可以依附其現有的代碼完成深拷貝,還可以本身界說一些克隆的方法和不須要克隆的類型,靈巧性強。
4. 幾種拷貝方法速度的比擬
上述3中形式都可以完成深拷貝,那種拷貝的方法速度最快是我們所關懷的。
先上測試代碼:
 

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

    //挪用二方庫
    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));

    //挪用Cloneable接話柄現的clone函數
    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));

    //序列化與反序列化生成新對象
    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));
  }


運轉成果的單元為毫秒(此數據疏忽不盤算java熱門和能夠的gc)。

從這個表可以得出結論:

1、完成Cloneable接口的拷貝是最快的,由於他只觸及到了內存拷貝,然則假如觸及的屬性為通俗對象比擬多的時刻寫起來費事點
2、序列化/反序列化拷貝最慢
3、應用cloning庫,因為應用了遞歸和反射機制絕對Cloneable接話柄現的拷貝要慢,但比序列化方法要快。

以上就是本文的全體內容,願望對年夜家的進修有所贊助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved