java中關(guān)于深拷貝的幾種方式總結(jié)
前言
在java里,當(dāng)我們需要拷貝一個(gè)對(duì)象時(shí),有兩種類型的拷貝:淺拷貝與深拷貝。
- 淺拷貝只是拷貝了源對(duì)象的地址,所以源對(duì)象的值發(fā)生變化時(shí),拷貝對(duì)象的值也會(huì)發(fā)生變化。
- 深拷貝則是拷貝了源對(duì)象的所有值,所以即使源對(duì)象的值發(fā)生變化時(shí),拷貝對(duì)象的值也不會(huì)改變。
方式1:構(gòu)造函數(shù)深拷貝
我們可以調(diào)用構(gòu)造函數(shù)進(jìn)行深拷貝,形參如果是基本類型和字符串則是直接賦值,如果是對(duì)象,則是重新new一個(gè)。
測(cè)試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Getter; /** * @author 凌兮 * @date 2021/4/15 14:28 * 通過(guò)構(gòu)造器進(jìn)行深拷貝測(cè)試 */ @Getter public class UserConstruct { private String userName; private AddressConstruct address; public UserConstruct() { } public UserConstruct(String userName, AddressConstruct address) { this.userName = userName; this.address = address; } public static void main(String[] args) { AddressConstruct address = new AddressConstruct("小區(qū)1", "小區(qū)2"); UserConstruct user = new UserConstruct("小李", address); // 調(diào)用構(gòu)造函數(shù)進(jìn)行深拷貝 UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2())); // 修改源對(duì)象的值 user.getAddress().setAddress1("小區(qū)3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1()); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); // true System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2())); } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:28 */ @Getter @Setter public class AddressConstruct { private String address1; private String address2; public AddressConstruct() { } public AddressConstruct(String address1, String address2) { this.address1 = address1; this.address2 = address2; } }
方式2:重載Clone()方法深拷貝
Object父類有個(gè)clone()的拷貝方法,不過(guò)它是protected類型的 ,我們需要重寫(xiě)它并修改為public類型,除此之外,子類還需要實(shí)現(xiàn)Cloneable接口來(lái)告訴JVM這個(gè)類上市可以拷貝的。
測(cè)試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:49 * */ @Setter @Getter public class AddressClone implements Cloneable{ private String address1; private String address2; public AddressClone() { } public AddressClone(String address1, String address2) { this.address1 = address1; this.address2 = address2; } @Override protected AddressClone clone() throws CloneNotSupportedException { return (AddressClone) super.clone(); } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /** * @author 凌兮 * @date 2021/4/15 14:48 * 通過(guò)實(shí)現(xiàn)Clone接口實(shí)現(xiàn)深拷貝 */ @Setter @Getter public class UserClone implements Cloneable{ private String userName; private AddressClone address; public UserClone() { } public UserClone(String userName, AddressClone address) { this.userName = userName; this.address = address; } /** * Object父類有個(gè)clone()的拷貝方法,不過(guò)它是protected類型的, * 我們需要重寫(xiě)它并修改為public類型。除此之外, * 子類還需要實(shí)現(xiàn)Cloneable接口來(lái)告訴JVM這個(gè)類是可以拷貝的。 * @return * @throws CloneNotSupportedException */ @Override protected UserClone clone() throws CloneNotSupportedException { // 需要注意的是,super.clone()其實(shí)是淺拷貝, // 所以在重寫(xiě)UserClone類的clone()方法時(shí),address對(duì)象需要調(diào)用address.clone()重新賦值 UserClone userClone = (UserClone) super.clone(); userClone.setAddress(this.address.clone()); return userClone; } public static void main(String[] args) throws CloneNotSupportedException { AddressClone address = new AddressClone("小區(qū)1", "小區(qū)2"); UserClone user = new UserClone("小李", address); UserClone copyUser = user.clone(); user.getAddress().setAddress1("小區(qū)3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
需要注意的是,super.clone()其實(shí)是淺拷貝,所以在重寫(xiě)User類的clone()方法時(shí),address對(duì)象需要調(diào)用address.clone()重新賦值。
方式3:Apache Commons Lang序列化方式深拷貝
Java提供了序列化的能力,我們可以先將源對(duì)象進(jìn)行序列化,再反序列化生成拷貝對(duì)象。但是,使用序列化的前提是拷貝的類(包括其成員變量)需要實(shí)現(xiàn)Serializable接口。
Apache Commons Lang包對(duì)Java序列化進(jìn)行了封裝,我們可以直接使用它。
測(cè)試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import java.io.Serializable; /** * @author 凌兮 * @date 2021/4/15 15:11 */ @Getter @Setter public class AddressSerializable implements Serializable { private String address1; private String address2; public AddressSerializable() { } public AddressSerializable(String address1, String address2) { this.address1 = address1; this.address2 = address2; } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.SerializationUtils; import java.io.Serializable; /** * @author 凌兮 * @date 2021/4/15 15:10 * 通過(guò)Apache Commons Lang 序列化方式深拷貝 * Java提供了序列化的能力,我們可以先將源對(duì)象進(jìn)行序列化,再反序列化生成拷貝對(duì)象。 * 但是,使用序列化的前提是拷貝的類(包括其成員變量)需要實(shí)現(xiàn)Serializable接口。 * Apache Commons Lang包對(duì)Java序列化進(jìn)行了封裝,我們可以直接使用它。 */ @Getter @Setter public class UserSerializable implements Serializable { private String userName; private AddressSerializable address; public UserSerializable() { } public UserSerializable(String userName, AddressSerializable address) { this.userName = userName; this.address = address; } public static void main(String[] args) { AddressSerializable address = new AddressSerializable("小區(qū)1", "小區(qū)2"); UserSerializable user = new UserSerializable("小李", address); UserSerializable copyUser = SerializationUtils.clone(user); user.getAddress().setAddress1("小區(qū)3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
方式4:Gson序列化方式深拷貝
Gson可以將對(duì)象序列化成JSON,也可以將JSON反序列化成對(duì)象,所以我們可以用它進(jìn)行深拷貝。
測(cè)試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Data; /** * @author 凌兮 * @date 2021/4/15 15:31 */ @Data public class AddressGson { private String address1; private String address2; public AddressGson() { } public AddressGson(String address1, String address2) { this.address1 = address1; this.address2 = address2; } } package com.lyj.demo.pojo.cloneTest; import com.google.gson.Gson; import lombok.Data; /** * @author 凌兮 * @date 2021/4/15 15:30 * 使用Gson序列化方式進(jìn)行深拷貝 * Gson可以將對(duì)象序列化成JSON,也可以將JSON反序列化成對(duì)象,所以我們可以用它進(jìn)行深拷貝 */ @Data public class UserGson { private String userName; private AddressGson address; public UserGson() { } public UserGson(String userName, AddressGson address) { this.userName = userName; this.address = address; } public static void main(String[] args) { AddressGson address = new AddressGson("小區(qū)1", "小區(qū)2"); UserGson user = new UserGson("小李", address); // 使用Gson序列化進(jìn)行深拷貝 Gson gson = new Gson(); UserGson copyUser = gson.fromJson(gson.toJson(user), UserGson.class); user.getAddress().setAddress1("小區(qū)3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
方式5:Jackson序列化方式
Jackson與Gson相似,可以將對(duì)象序列化成JSON,明顯不同的地方是拷貝的類(包括其成員變量)需要有默認(rèn)的無(wú)參構(gòu)造函數(shù)。
測(cè)試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Data; /** * @author 凌兮 * @date 2021/4/15 15:41 */ @Data public class AddressJackson { private String address1; private String address2; public AddressJackson() { } public AddressJackson(String address1, String address2) { this.address1 = address1; this.address2 = address2; } } package com.lyj.demo.pojo.cloneTest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; /** * @author 凌兮 * @date 2021/4/15 15:40 * 通過(guò)Jackson方式實(shí)現(xiàn)深拷貝 * Jackson與Gson相似,可以將對(duì)象序列化成JSON,明顯不同的地方是拷貝的類(包括其成員變量)需要有默認(rèn)的無(wú)參構(gòu)造函數(shù)。 */ @Data public class UserJackson { private String userName; private AddressJackson address; public UserJackson() { } public UserJackson(String userName, AddressJackson address) { this.userName = userName; this.address = address; } public static void main(String[] args) throws JsonProcessingException { AddressJackson address = new AddressJackson("小區(qū)1", "小區(qū)2"); UserJackson user = new UserJackson("小李", address); // 使用Jackson序列化進(jìn)行深拷貝 ObjectMapper objectMapper = new ObjectMapper(); UserJackson copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), UserJackson.class); user.getAddress().setAddress1("小區(qū)3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
總結(jié)
深拷貝方法 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|
構(gòu)造函數(shù) | 1. 底層實(shí)現(xiàn)簡(jiǎn)單 2. 不需要引入第三方包 3. 系統(tǒng)開(kāi)銷小 4. 對(duì)拷貝類沒(méi)有要求,不需要實(shí)現(xiàn)額外接口和方法 | 1. 可用性差,每次新增成員變量都需要新增新的拷貝構(gòu)造函數(shù) |
重載clone()方法 | 1. 底層實(shí)現(xiàn)較簡(jiǎn)單 2. 不需要引入第三方包 3. 系統(tǒng)開(kāi)銷小 | 1. 可用性較差,每次新增成員變量可能需要修改clone()方法 2. 拷貝類(包括其成員變量)需要實(shí)現(xiàn)Cloneable接口 |
Apache Commons Lang序列化 | 1. 可用性強(qiáng),新增成員變量不需要修改拷貝方法 | 1. 底層實(shí)現(xiàn)較復(fù)雜 2. 需要引入Apache Commons Lang第三方JAR包 3. 拷貝類(包括其成員變量)需要實(shí)現(xiàn)Serializable接口 4. 序列化與反序列化存在一定的系統(tǒng)開(kāi)銷 |
Gson序列化 | 1. 可用性強(qiáng),新增成員變量不需要修改拷貝方法 2. 對(duì)拷貝類沒(méi)有要求,不需要實(shí)現(xiàn)額外接口和方法 | 1. 底層實(shí)現(xiàn)復(fù)雜 2. 需要引入Gson第三方JAR包 3. 序列化與反序列化存在一定的系統(tǒng)開(kāi)銷 |
Jackson序列化 | 1. 可用性強(qiáng),新增成員變量不需要修改拷貝方法 | 1. 底層實(shí)現(xiàn)復(fù)雜 2. 需要引入Jackson第三方JAR包 3. 拷貝類(包括其成員變量)需要實(shí)現(xiàn)默認(rèn)的無(wú)參構(gòu)造函數(shù) 4. 序列化與反序列化存在一定的系統(tǒng)開(kāi)銷 |
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java基于Runtime調(diào)用外部程序出現(xiàn)阻塞的解決方法
這篇文章主要介紹了Java基于Runtime調(diào)用外部程序出現(xiàn)阻塞的解決方法,是一個(gè)非常實(shí)用的技巧,需要的朋友可以參考下2014-09-09SpringBoot如何進(jìn)行業(yè)務(wù)校驗(yàn)實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于SpringBoot如何進(jìn)行業(yè)務(wù)校驗(yàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01Spring Boot自定義配置屬性源(PropertySource)
這篇文章主要介紹了Spring Boot自定義配置屬性源(PropertySource),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Spring Cloud Ribbon實(shí)現(xiàn)客戶端負(fù)載均衡的方法
本篇文章主要介紹了Spring Cloud Ribbon實(shí)現(xiàn)客戶端負(fù)載均衡的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05