日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

深度拷贝 java_Java深度拷贝方式和性能对比

發(fā)布時(shí)間:2025/3/19 java 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深度拷贝 java_Java深度拷贝方式和性能对比 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

Java的深度拷貝大致分為克隆(實(shí)現(xiàn)Java的Clone接口)和序列化(實(shí)現(xiàn)Java的Serializable接口)兩種,但是基于不同的序列化方式,有可以延伸出幾種方式。下面分析一下每種的注意事項(xiàng)和性能對(duì)比【當(dāng)前電腦為4核16G,只是當(dāng)前使用main方法單線(xiàn)程測(cè)試】。

一、拷貝和深淺拷貝

可以使用Java native方法提供的Clone方式進(jìn)行對(duì)象的拷貝,其性能是最高的,甚至高過(guò)new 關(guān)鍵字。使用new關(guān)鍵字創(chuàng)建對(duì)象,如果是第一次創(chuàng)建則會(huì)經(jīng)歷類(lèi)加載機(jī)制的雙親委派(加載、驗(yàn)證、準(zhǔn)備、解析、初始化)。即使非第一次創(chuàng)建也會(huì)經(jīng)歷(常量池判斷,內(nèi)存分配,值初始化,init方法調(diào)用,棧中對(duì)象的引用等)等過(guò)程。

我們需要繼承自Clone接口,重寫(xiě)Object的clone方法。如下:

public class DeepCopyEntity implements Cloneable {

@Override

protected DeepCopyEntity clone() {

try {

return (DeepCopyEntity)super.clone();

} catch (CloneNotSupportedException e) {

log.info("沒(méi)有實(shí)現(xiàn)克隆接口");

return null;

}

}

}

但是我們?cè)谑褂玫臅r(shí)候,需要每個(gè)對(duì)象都編寫(xiě)這樣的代碼。可以?xún)?yōu)化為繼承自類(lèi)似下面的 CloneSupport 類(lèi)(前體是沒(méi)有繼承其他的類(lèi)):

public class CloneSupport implements Cloneable {

@SuppressWarnings("unchecked")

@Override

public T clone() {

try {

return (T) super.clone();

} catch (CloneNotSupportedException e) {

throw new CloneRuntimeException(e);

}

}

}

但是即使是克隆之后的對(duì)象也是淺拷貝。即對(duì)象的屬性如果是非基本數(shù)據(jù)類(lèi)型和String的情況下,新老對(duì)象的對(duì)象屬性的內(nèi)存地址任然相同,則任何一個(gè)對(duì)象改變其值之后,另一個(gè)對(duì)象的值也就是改變了,這很多時(shí)候可能是我們不想要的。那么需要進(jìn)行深度的拷貝。則需要其屬性對(duì)象的類(lèi)也繼承自Clone接口,并且重新clone方法。如下(是我項(xiàng)目中使用的):

public class PurOrderSkuBO implements Serializable, Cloneable {

@Override

public PurOrderSkuBO clone() {

try {

final PurOrderSkuBO clone = (PurOrderSkuBO) super.clone();

clone.purOrderSkuDTO = purOrderSkuDTO.clone();

clone.productList = productList.stream().map(PurOrderItemBO::clone).collect(Collectors.toList());

return clone;

} catch (CloneNotSupportedException e) {

return new PurOrderSkuBO();

}

}

private PurOrderSkuDTO purOrderSkuDTO;

private List productList;

}

public class PurOrderSkuDTO extends CloneSupport {

}

二、序列化

另一種實(shí)現(xiàn)深度拷貝的方式就是序列化,無(wú)論是Jdk的序列化還是其他方式的序列化都需要實(shí)現(xiàn)自 java.io.Serializable接口,并且設(shè)置自己的serialVersionUID,并且保證項(xiàng)目中不能有相同的值(很多開(kāi)發(fā)的時(shí)候,基于原來(lái)的類(lèi)copy過(guò)來(lái)后需要進(jìn)行修改),如下:

public class DeepCopyEntity implements Cloneable, Serializable {

private static final long serialVersionUID = 6172279441386879379L;

}

三、深度拷貝的方式

1、new關(guān)鍵字

實(shí)現(xiàn)對(duì)象的深度拷貝,就是對(duì)象的每一層屬性的內(nèi)存地址都不相同,那么基于new 對(duì)象,再每一層設(shè)置new的屬性對(duì)象。也是可以實(shí)現(xiàn)的,或者基于反射的方式,并且性能也是比較高的。需要注意jdk 6及之前的反射性能比較差。

優(yōu)點(diǎn):性能高,缺點(diǎn):就是每個(gè)對(duì)象都需要new,并且每一層都需要用setter等進(jìn)行賦值【硬編碼】。

2、Clone

優(yōu)點(diǎn):性能高,缺點(diǎn):所有層級(jí)只要有屬性對(duì)象就需要實(shí)現(xiàn)Clone,并且重寫(xiě)clone方法。如果對(duì)象有七八層,其中每一層的每一個(gè)地方?jīng)]有注意到就可能非深拷貝。

3、jdk序列化

jdk序列化只需要基于ObjectOutputStream將原對(duì)象流寫(xiě)出去(寫(xiě)入本地磁盤(pán)),再基于ObjectInputStream將對(duì)象流讀回來(lái)即可。如下:

/**

* 深層拷貝 - 需要類(lèi)繼承序列化接口

* @param 對(duì)象類(lèi)型

* @param obj 原對(duì)象

* @return 深度拷貝的對(duì)象

* @throws Exception

* @see java.io.Closeable

* @see AutoCloseable 不用進(jìn)行關(guān)閉

*/

@SuppressWarnings("unchecked")

public static T copyImplSerializable(T obj) throws Exception {

ByteArrayOutputStream baos = null;

ObjectOutputStream oos = null;

ByteArrayInputStream bais = null;

ObjectInputStream ois = null;

Object o = null;

//如果子類(lèi)沒(méi)有繼承該接口,這一步會(huì)報(bào)錯(cuò)

try {

baos = new ByteArrayOutputStream();

oos = new ObjectOutputStream(baos);

oos.writeObject(obj);

bais = new ByteArrayInputStream(baos.toByteArray());

ois = new ObjectInputStream(bais);

o = ois.readObject();

return (T) o;

} catch (Exception e) {

throw new Exception("對(duì)象中包含沒(méi)有繼承序列化的對(duì)象");

}

}

優(yōu)點(diǎn):不需要像克隆和new一樣單獨(dú)開(kāi)發(fā),缺點(diǎn):性能比較差

4、kyro序列化

kyro需要單獨(dú)引入maven依賴(lài),如:

com.esotericsoftware

kryo

5.0.0-RC9

使用時(shí)需要?jiǎng)?chuàng)建 Kryo對(duì)象【 Kryo kryo = new Kryo(); 】,只是該對(duì)象是非線(xiàn)程安全的,所有如果在項(xiàng)目中使用時(shí),最好放到ThreadLocal中進(jìn)行創(chuàng)建。使用就比較簡(jiǎn)單了:

public static T copyByKryo(T source){

return kryo.copy(source);

}

優(yōu)點(diǎn):性能較高, 缺點(diǎn):需要單獨(dú)引入maven,性能比new 和clone的低一點(diǎn)

5、Json序列化

項(xiàng)目上使用Json 進(jìn)行 redis、rpc調(diào)用(如 Spring Cloud Feign) 進(jìn)行序列化和反序列化是比較常用的,但是如果僅僅是本地深度拷貝,則使用該方式性能是最差的。可以在下面進(jìn)行比較,各種json框架的序列化方式都差不多。

四、性能對(duì)比

創(chuàng)建一個(gè)50個(gè)字段的對(duì)象,并使用不同的深度拷貝方式,創(chuàng)建對(duì)象N多遍。

@Data

@NoArgsConstructor

@AllArgsConstructor

public class DeepCopyEntity implements Cloneable, Serializable {

/**

* 序列化標(biāo)識(shí)

*/

private static final long serialVersionUID = 6172279441386879379L;

@Override

protected DeepCopyEntity clone() {

try {

return (DeepCopyEntity)super.clone();

} catch (CloneNotSupportedException e) {

e.printStackTrace();

return null;

}

}

private String id;

private String field1;

private String field2;

private String field3;

private String field4;

private String field5;

private String field6;

private String field7;

private String field8;

private String field9;

private String field10;

private String field11;

private String field12;

private String field13;

private String field14;

private String field15;

private String field16;

private String field17;

private String field18;

private String field19;

private String field20;

private String field21;

private String field22;

private String field23;

private String field24;

private String field25;

private String field26;

private String field27;

private String field28;

private String field29;

private String field30;

private String field31;

private String field32;

private String field33;

private String field34;

private String field35;

private String field36;

private String field37;

private String field38;

private String field39;

private String field40;

private String field41;

private String field42;

private String field43;

private String field44;

private String field45;

private String field46;

private String field47;

private String field48;

private String field49;

private String field50;

}

package com.kevin.deepcopy;

import com.esotericsoftware.kryo.Kryo;

import net.sf.json.JSONObject;

import org.springframework.util.StopWatch;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

/**

* 深度拷貝類(lèi)型 循環(huán)次數(shù)[1000] 循環(huán)次數(shù)[10000] 循環(huán)次數(shù)[1000000]

* new 5 ms 14 ms 133 ms

*

* Cloneable: < 1 ms 7 ms 88 ms

*

* Jdk序列化: 272 ms 1589 ms 66190 ms

*

* Kryo序列化: 95 ms 123 ms 2438 ms

*

* Json序列化: 1203 ms 3746 ms 163512 ms

*

* 總結(jié): 1)、序列化性能 Clone > new > Kryo序列化 > Jdk序列化 > Json(各種Json類(lèi)似)序列化

* 2)、Clone深拷貝性能最高,但是如果屬性中有特定的對(duì)象字段,則需要自己編寫(xiě)代碼

* 3)、new 性能僅次于Clone,因?yàn)樾枰獔?zhí)行Jvm過(guò)程(常量池判斷,內(nèi)存分配,值初始化,init方法調(diào)用,棧中對(duì)象的引用等),并且主要是每個(gè)對(duì)象需要單獨(dú)編寫(xiě)代碼,當(dāng)然也不建議使用反射

* 4)、kryo 性能較高,并且不需要單獨(dú)的開(kāi)發(fā), 若對(duì)性能不是特別高,可以考慮使用.(kryo是非線(xiàn)程安全的,項(xiàng)目中使用時(shí)可以放入ThreadLocal中)

* 5)、Jdk序列化和Json序列化,性能太低,高性能項(xiàng)目不建議使用

*

* 總結(jié)的總結(jié): 如果性能要求特別高(或者對(duì)象結(jié)構(gòu)層次不深),可以使用Clone方式;否則可以考慮使用 Kryo序列化和反序列化實(shí)現(xiàn)對(duì)象深拷貝

*

* @author kevin

* @date 2020/9/27 13:45

* @since 1.0.0

*/

public class DeepCopyTest {

/**

* 循環(huán)的次數(shù)

*/

private static final int LOOP = 1000;

private static Kryo kryo = new Kryo();

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

DeepCopyEntity demo = getInit();

StopWatch stopWatch = new StopWatch("測(cè)試深拷貝");

stopWatch.start();

for (int i = 0; i < LOOP; i++) {

// DeepCopyEntity deep = newObject(demo);

final DeepCopyEntity deep = demo.clone();

// final DeepCopyEntity deepCopyEntity = copyImplSerializable(demo);

// final DeepCopyEntity deepCopyEntity = copyByKryo(demo);

// final DeepCopyEntity deepCopyEntity1 = copyByJson(demo);

}

stopWatch.stop();

System.out.println(stopWatch.prettyPrint());

}

/**

* 深層拷貝 - 需要net.sf.json.JSONObject

* @param

* @param obj

* @return

* @throws Exception

*/

@SuppressWarnings("unchecked")

public static T copyByJson(T obj) throws Exception {

return (T) JSONObject.toBean(JSONObject.fromObject(obj),obj.getClass());

}

/**

*

* @param source

* @return

*/

public static DeepCopyEntity copyByKryo(DeepCopyEntity source){

return kryo.copy(source);

}

/**

* 深層拷貝 - 需要類(lèi)繼承序列化接口

* @param

* @param obj

* @return

* @throws Exception

* @see java.io.Closeable

* @see AutoCloseable 不用進(jìn)行關(guān)閉

*/

@SuppressWarnings("unchecked")

public static T copyImplSerializable(T obj) throws Exception {

ByteArrayOutputStream baos = null;

ObjectOutputStream oos = null;

ByteArrayInputStream bais = null;

ObjectInputStream ois = null;

Object o = null;

//如果子類(lèi)沒(méi)有繼承該接口,這一步會(huì)報(bào)錯(cuò)

try {

baos = new ByteArrayOutputStream();

oos = new ObjectOutputStream(baos);

oos.writeObject(obj);

bais = new ByteArrayInputStream(baos.toByteArray());

ois = new ObjectInputStream(bais);

o = ois.readObject();

return (T) o;

} catch (Exception e) {

throw new Exception("對(duì)象中包含沒(méi)有繼承序列化的對(duì)象");

}

}

private static DeepCopyEntity newObject(DeepCopyEntity demo) {

final DeepCopyEntity deepCopyEntity = new DeepCopyEntity();

deepCopyEntity.setId(demo.getId());

deepCopyEntity.setField1(demo.getField1());

deepCopyEntity.setField2(demo.getField2());

deepCopyEntity.setField3(demo.getField1());

deepCopyEntity.setField4(demo.getField1());

deepCopyEntity.setField5(demo.getField1());

deepCopyEntity.setField6(demo.getField1());

deepCopyEntity.setField7(demo.getField1());

deepCopyEntity.setField8(demo.getField1());

deepCopyEntity.setField9(demo.getField1());

deepCopyEntity.setField10(demo.getField1());

deepCopyEntity.setField11(demo.getField1());

deepCopyEntity.setField12(demo.getField1());

deepCopyEntity.setField13(demo.getField1());

deepCopyEntity.setField14(demo.getField1());

deepCopyEntity.setField15(demo.getField1());

deepCopyEntity.setField16(demo.getField1());

deepCopyEntity.setField17(demo.getField1());

deepCopyEntity.setField18(demo.getField1());

deepCopyEntity.setField19(demo.getField1());

deepCopyEntity.setField20(demo.getField1());

deepCopyEntity.setField21(demo.getField1());

deepCopyEntity.setField22(demo.getField1());

deepCopyEntity.setField23(demo.getField1());

deepCopyEntity.setField24(demo.getField1());

deepCopyEntity.setField25(demo.getField1());

deepCopyEntity.setField26(demo.getField1());

deepCopyEntity.setField27(demo.getField1());

deepCopyEntity.setField28(demo.getField1());

deepCopyEntity.setField29(demo.getField1());

deepCopyEntity.setField30(demo.getField1());

deepCopyEntity.setField31(demo.getField1());

deepCopyEntity.setField32(demo.getField1());

deepCopyEntity.setField33(demo.getField1());

deepCopyEntity.setField34(demo.getField1());

deepCopyEntity.setField35(demo.getField1());

deepCopyEntity.setField36(demo.getField1());

deepCopyEntity.setField37(demo.getField1());

deepCopyEntity.setField38(demo.getField1());

deepCopyEntity.setField39(demo.getField1());

deepCopyEntity.setField40(demo.getField1());

deepCopyEntity.setField41(demo.getField1());

deepCopyEntity.setField42(demo.getField1());

deepCopyEntity.setField43(demo.getField1());

deepCopyEntity.setField44(demo.getField1());

deepCopyEntity.setField45(demo.getField1());

deepCopyEntity.setField46(demo.getField1());

deepCopyEntity.setField47(demo.getField1());

deepCopyEntity.setField48(demo.getField1());

deepCopyEntity.setField49(demo.getField1());

deepCopyEntity.setField50(demo.getField1());

return deepCopyEntity;

}

/**

* 獲取初始化值

* @return demo對(duì)象

*/

private static DeepCopyEntity getInit() {

final DeepCopyEntity deepCopyEntity = new DeepCopyEntity();

deepCopyEntity.setId("測(cè)試字段進(jìn)來(lái)撒個(gè)是個(gè)是個(gè)復(fù)活節(jié)快樂(lè)時(shí)刻六公里按時(shí)交付格拉斯可根據(jù)ask了接受了嘎嘎健康金克拉是個(gè)零售價(jià)格克拉斯關(guān)鍵時(shí)刻兩個(gè)jklsghbld時(shí)間噶設(shè)立國(guó)家級(jí)法國(guó)設(shè)計(jì)規(guī)劃拉薩盡快趕回監(jiān)考老師的風(fēng)格就是看來(lái)撒骨灰兩個(gè)據(jù)類(lèi)");

// 省略后面所有字段的設(shè)置,都設(shè)置一樣的字段 ......

return deepCopyEntity;

}

}

總結(jié):

1)、序列化性能 Clone > new > Kryo序列化 > Jdk序列化 > Json(各種Json類(lèi)似)序列化

2)、Clone深拷貝性能最高,但是如果屬性中有特定的對(duì)象字段,則需要自己編寫(xiě)代碼

3)、new 性能僅次于Clone,因?yàn)樾枰獔?zhí)行Jvm過(guò)程(常量池判斷,內(nèi)存分配,值初始化,init方法調(diào)用,棧中對(duì)象的引用等),

并且主要是每個(gè)對(duì)象需要單獨(dú)編寫(xiě)代碼,當(dāng)然也不建議使用反射

4)、kryo 性能較高,并且不需要單獨(dú)的開(kāi)發(fā), 若對(duì)性能不是特別高,可以考慮使用.

kryo是非線(xiàn)程安全的,項(xiàng)目中使用時(shí)可以放入ThreadLocal中

5)、Jdk序列化和Json序列化,性能太低,高性能項(xiàng)目不建議使用

如果性能要求特別高(或者對(duì)象結(jié)構(gòu)層次不深),可以使用Clone方式;

否則可以考慮使用 Kryo序列化和反序列化實(shí)現(xiàn)對(duì)象深拷貝

結(jié)尾

本文到這里就結(jié)束了,感謝看到最后的朋友,都看到最后了,點(diǎn)個(gè)贊再走啊,如有不對(duì)之處還請(qǐng)多多指正。

總結(jié)

以上是生活随笔為你收集整理的深度拷贝 java_Java深度拷贝方式和性能对比的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。