Java的淺拷貝與深拷貝
一、概念
- 淺拷貝
- 淺拷貝僅僅復制所考慮的對象(包括對象中的基本變量),而不復制它所引用的對象。
- 深拷貝
- 深拷貝把要復制的對象所引用的對象都復制了一遍,并拷貝屬性指向的動態(tài)分配的內存。
- 簡單描述一下,就是淺拷貝引用了同一個對象地址,而深拷貝卻是一個新的對象。
二、問題來源
在一個web請求接口中,返回對象多次封裝了同一個結合,導致返回json中的內容體出現(xiàn)了$ref。
-
問題重現(xiàn):
-
定義一個Student(String name,Age age),Age(Integer age)類
public static void main(String[] args) { Student student1 = new Student("張三",new Age(14)); Student student2 = new Student("李四",new Age(15)); List son = Arrays.asList(student1,student2); List<List> father = new ArrayList<>(); father.add(son); father.add(son); System.out.println(JSON.toJSONString(father)); System.out.println(JSON.toJSONString(father, SerializerFeature.DisableCircularReferenceDetect)); } //[[{"age":14,"name":"張三"},{"age":15,"name":"李四"}],{"$ref":"$[0]"}] //[[{"age":14,"name":"張三"},{"age":15,"name":"李四"}],[{"age":14,"name":"張三"},{"age":15,"name":"李四"}]]
-
-
FastJson循環(huán)引用問題
-
解決方案一:
-
簡單轉換
System.out.println(JSON.toJSONString(father, SerializerFeature.DisableCircularReferenceDetect)); -
配置SpringBoot項目中的json序列化配置
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
-
-
解決方案二:
- 創(chuàng)建新對象,不循環(huán)引用。
-
當然,最好的解決辦法是創(chuàng)建對象,F(xiàn)astJson提供了一個監(jiān)測機制,防止循環(huán)解析導致StackOverflowError。某一次的問題可以判斷為不是循環(huán)引用,但無法保證下一次的循環(huán)引用。把監(jiān)測關了,下一次就可能出現(xiàn)StackOverflowError。避免這個問題,就要創(chuàng)建新的對象。于是就來到了數(shù)據(jù)的拷貝。
三、拷貝
先定義一個基本對象
public class CopyObject implements Cloneable{
// 基本類型
private String name;
// 引用類型
private StringBuilder add;
public CopyObject(String name, StringBuilder add) {
this.name = name;
this.add = add;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public StringBuilder getAdd() {
return add;
}
public void setAdd(StringBuilder add) {
this.add = add;
}
// 淺拷貝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
3.1 淺拷貝
復制基本數(shù)據(jù)類型
-
引用老的引用數(shù)據(jù)類型
Student、Age類實現(xiàn)CloneAble接口,重寫實現(xiàn)clone() @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }public static void main(String[] args) throws CloneNotSupportedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { Age age = new Age(13); Student source = new Student("a", age); Student target = (Student) source.clone(); Student target = SerializationUtils.clone(source); Student target = JSONObject.parseObject(JSONObject.toJSONString(source), Student.class); System.out.println("step1 ====================="); System.out.println("source:" + source.getName() + " " + source.getAge().getAge()); //source:a 13 System.out.println("target:" + target.getName() + " " + target.getAge().getAge()); target.setName("b"); //target:a 13 target.getAge().setAge(15); System.out.println("step2 ====================="); System.out.println("source:" + source.getName() + " " + source.getAge().getAge()); //source:a 15 System.out.println("target:" + target.getName() + " " + target.getAge().getAge()); //target:a 15 } -
圖解:
ShallowCopy
3.1 深拷貝
-
復制所有的的引用數(shù)據(jù)類型
重寫Student的實現(xiàn)clone() @Override protected Student clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); student.setAge((Age) age.clone()); return student; }public static void main(String[] args) throws CloneNotSupportedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { Age age = new Age(13); Student source = new Student("a", age); // Student target = (Student) source.clone(); // Student target = SerializationUtils.clone(source); Student target = JSONObject.parseObject(JSONObject.toJSONString(source), Student.class); System.out.println("step1 ====================="); System.out.println("source:" + source.getName() + " " + source.getAge().getAge()); //source:a 13 System.out.println("target:" + target.getName() + " " + target.getAge().getAge()); //target:a 13 target.setName("b"); target.getAge().setAge(15); System.out.println("step2 ====================="); System.out.println("source:" + source.getName() + " " + source.getAge().getAge()); //source:a 13 System.out.println("target:" + target.getName() + " " + target.getAge().getAge()); //target:b 15 } -
圖解:
DeepCopy
四、實現(xiàn)
-
淺拷貝
- 默認Object就帶有clone方法,但是屬于protect。
- 需要實現(xiàn)Cloneable工具類之后,重現(xiàn)clone方法。
- 也有許多其他的工具類提供序列化,使用的時候一定要注意原理,再使用。
-
深拷貝
- 序列化與反序列化的機制存在下,會保證對象完成不一樣。
-
利用common-lang序列化工具clone工具:
// Student、Age需要實現(xiàn)Serializable Student targetShallow = SerializationUtils.clone(source); -
對于復雜的數(shù)組對象無法直接序列化,也可使用JSON序列化之后,再反序列化
Student target = JSONObject.parseObject(JSONObject.toJSONString(source), Student.class);
-
【強制】避免用 Apache Beanutils 進行屬性的 copy。(阿里巴巴Java開發(fā)手冊)
- Apache BeanUtils 性能較差,可以使用其他方案比如 Spring BeanUtils, Cglib BeanCopier,注意 均是淺拷貝。
本篇文章由一文多發(fā)平臺ArtiPub自動發(fā)布

