在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中深拷貝的方式有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!