您好,登錄后才能下訂單哦!
本篇內容介紹了“java中深拷貝的方式有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在java里,當我們需要拷貝一個對象時,有兩種類型的拷貝:淺拷貝與深拷貝。
淺拷貝只是拷貝了源對象的地址,所以源對象的值發生變化時,拷貝對象的值也會發生變化。
深拷貝則是拷貝了源對象的所有值,所以即使源對象的值發生變化時,拷貝對象的值也不會改變。
我們可以調用構造函數進行深拷貝,形參如果是基本類型和字符串則是直接賦值,如果是對象,則是重新new一個。
測試案例
package com.lyj.demo.pojo.cloneTest; import lombok.Getter; /** * @author 凌兮 * @date 2021/4/15 14:28 * 通過構造器進行深拷貝測試 */ @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("小區1", "小區2"); UserConstruct user = new UserConstruct("小李", address); // 調用構造函數進行深拷貝 UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2())); // 修改源對象的值 user.getAddress().setAddress1("小區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; } }
Object父類有個clone()的拷貝方法,不過它是protected類型的 ,我們需要重寫它并修改為public類型,除此之外,子類還需要實現Cloneable接口來告訴JVM這個類上市可以拷貝的。
測試案例
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 * 通過實現Clone接口實現深拷貝 */ @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父類有個clone()的拷貝方法,不過它是protected類型的, * 我們需要重寫它并修改為public類型。除此之外, * 子類還需要實現Cloneable接口來告訴JVM這個類是可以拷貝的。 * @return * @throws CloneNotSupportedException */ @Override protected UserClone clone() throws CloneNotSupportedException { // 需要注意的是,super.clone()其實是淺拷貝, // 所以在重寫UserClone類的clone()方法時,address對象需要調用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("小區1", "小區2"); UserClone user = new UserClone("小李", address); UserClone copyUser = user.clone(); user.getAddress().setAddress1("小區3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
需要注意的是,super.clone()其實是淺拷貝,所以在重寫User類的clone()方法時,address對象需要調用address.clone()重新賦值。
Java提供了序列化的能力,我們可以先將源對象進行序列化,再反序列化生成拷貝對象。但是,使用序列化的前提是拷貝的類(包括其成員變量)需要實現Serializable接口。
Apache Commons Lang包對Java序列化進行了封裝,我們可以直接使用它。
測試案例
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 * 通過Apache Commons Lang 序列化方式深拷貝 * Java提供了序列化的能力,我們可以先將源對象進行序列化,再反序列化生成拷貝對象。 * 但是,使用序列化的前提是拷貝的類(包括其成員變量)需要實現Serializable接口。 * Apache Commons Lang包對Java序列化進行了封裝,我們可以直接使用它。 */ @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("小區1", "小區2"); UserSerializable user = new UserSerializable("小李", address); UserSerializable copyUser = SerializationUtils.clone(user); user.getAddress().setAddress1("小區3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
Gson可以將對象序列化成JSON,也可以將JSON反序列化成對象,所以我們可以用它進行深拷貝。
測試案例
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序列化方式進行深拷貝 * Gson可以將對象序列化成JSON,也可以將JSON反序列化成對象,所以我們可以用它進行深拷貝 */ @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("小區1", "小區2"); UserGson user = new UserGson("小李", address); // 使用Gson序列化進行深拷貝 Gson gson = new Gson(); UserGson copyUser = gson.fromJson(gson.toJson(user), UserGson.class); user.getAddress().setAddress1("小區3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
Jackson與Gson相似,可以將對象序列化成JSON,明顯不同的地方是拷貝的類(包括其成員變量)需要有默認的無參構造函數。
測試案例
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 * 通過Jackson方式實現深拷貝 * Jackson與Gson相似,可以將對象序列化成JSON,明顯不同的地方是拷貝的類(包括其成員變量)需要有默認的無參構造函數。 */ @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("小區1", "小區2"); UserJackson user = new UserJackson("小李", address); // 使用Jackson序列化進行深拷貝 ObjectMapper objectMapper = new ObjectMapper(); UserJackson copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), UserJackson.class); user.getAddress().setAddress1("小區3"); // false System.out.println(user == copyUser); // false System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1())); } }
深拷貝方法 | 優點 | 缺點 |
---|---|---|
構造函數 | 1. 底層實現簡單 2. 不需要引入第三方包 3. 系統開銷小 4. 對拷貝類沒有要求,不需要實現額外接口和方法 | 1. 可用性差,每次新增成員變量都需要新增新的拷貝構造函數 |
重載clone()方法 | 1. 底層實現較簡單 2. 不需要引入第三方包 3. 系統開銷小 | 1. 可用性較差,每次新增成員變量可能需要修改clone()方法 2. 拷貝類(包括其成員變量)需要實現Cloneable接口 |
Apache Commons Lang序列化 | 1. 可用性強,新增成員變量不需要修改拷貝方法 | 1. 底層實現較復雜 2. 需要引入Apache Commons Lang第三方JAR包 3. 拷貝類(包括其成員變量)需要實現Serializable接口 4. 序列化與反序列化存在一定的系統開銷 |
Gson序列化 | 1. 可用性強,新增成員變量不需要修改拷貝方法 2. 對拷貝類沒有要求,不需要實現額外接口和方法 | 1. 底層實現復雜 2. 需要引入Gson第三方JAR包 3. 序列化與反序列化存在一定的系統開銷 |
Jackson序列化 | 1. 可用性強,新增成員變量不需要修改拷貝方法 | 1. 底層實現復雜 2. 需要引入Jackson第三方JAR包 3. 拷貝類(包括其成員變量)需要實現默認的無參構造函數 4. 序列化與反序列化存在一定的系統開銷 |
“java中深拷貝的方式有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。