在上篇文章:Java基礎(十二)--clone()方法,我們簡單介紹了clone()的使用
clone()對於基本數據類型的拷貝是完全沒問題的,但是如果是引用數據類型呢?
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student implements Cloneable{ private int id; private String name; private int sex; private Score score; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class Score { private int math; private int chinese; }
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = (Student)student.clone(); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
從結果上看,clone默認實現的是淺拷貝,並沒有達到我們的預期。那么什么是深拷貝和淺拷貝?
深拷貝:在淺拷貝的基礎上,引用變量也進行了clone,並指向clone產生的新對象
淺拷貝:被復制對象的所有值屬性都含有與原來對象的相同,但是對象引用屬性仍然指向原來的對象
clone()如何實現深拷貝?
1、引用成員變量Score需要實現Cloneable接口,並且重寫Object的Clone()
2、自定義Student的Clone()
@Override protected Object clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); Score score = student.getScore(); student.setScore((Score)score.clone()); return student; }
結果:
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))
結論:
想要通過Clone()實現深拷貝,該對象必須要實現Cloneable接口,實現並且自定義clone(),把引用變量進行clone,然后引用變量對應的類也要實現Cloneable接口並且實現clone方法。
假如這個對象有很多個引用變量,都要實現clone接口,並且重寫clone(),而且該對象自定義clone(),真的不是太方便,我們還可以序列化來實現深拷貝。
序列化實現深拷貝
可以寫一個序列化實現深拷貝的工具類,兼容所有對象。
public class DeepCloneUtils { public static <T extends Serializable> T deepClone(T object) { T cloneObject = null; try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(object); objectOutputStream.close(); ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); cloneObject = (T)objectInputStream.readObject(); objectInputStream.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return cloneObject; } }
當前類和引用對象對應的類都要實現序列化
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = (Student)DeepCloneUtils.deepClone(student); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))
PS:
把當前對象寫入到一個字節流中,再從字節流中將其讀出來,這樣就可以創建一個新的對象了,並且該新對象與源對象引用之間沒有關聯。
序列化實現方式,省略了clone()內部自定義的過程,但是還是要實現序列化的(當前類及引用類)。
現在有一個問題,如果這個引用對象是第三方jar包呢,我們如果讓它實現Serializable和Cloneable接口,上述兩種解決方案沒法使用了,我們需要新的解決方案。
以下通過第三方jar包實現對象拷貝,不需要實現Serializable和Cloneable接口:
modelMapper、Spring中的BeanUtils、Commons-BeanUtils、cglib、orika等,那么哪些才是深拷貝?
modelMapper實現對象拷貝:
1、首先引用maven依賴
<dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>1.1.0</version> </dependency>
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); ModelMapper modelMapper = new ModelMapper(); Student student1 = new Student(); modelMapper.map(student, student1); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:證明ModelMapper實現的是淺拷貝。
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
Spring中的BeanUtils實現對象拷貝:
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = new Student(); BeanUtils.copyProperties(student, student1); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:Spring-BeanUtils實現的是淺拷貝。
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
Commons-BeanUtils實現對象拷貝:
1、首先引用maven依賴
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.0</version> </dependency>
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = new Student(); BeanUtils.copyProperties(student1, student); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:證明ModelMapper實現的是淺拷貝。
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
Cglib實現對象拷貝:
1、首先引用maven依賴
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.1</version> </dependency>
public static void main(String[] args) throws Exception{ Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = new Student(); BeanCopier beanCopier = BeanCopier.create(Student.class, Student.class, false); beanCopier.copy(student, student1, null); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:Cglib實現的依然是淺拷貝,感覺很扎心啊。。。
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
PS:網上有說cglib實現自定義轉換器可以實現深拷貝,但是我試驗下來還是不能,各位可以試驗一下,如果可以,請留言。。。
orika實現對象拷貝:
<dependency> <groupId>ma.glasnost.orika</groupId> <artifactId>orika-core</artifactId> <version>1.5.0</version> </dependency> </dependencies>
public static void main(String[] args) throws Exception{ MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); mapperFactory.classMap(Student.class, Student.class) .byDefault() .register(); ConverterFactory converterFactory = mapperFactory.getConverterFactory(); MapperFacade mapper = mapperFactory.getMapperFacade(); Student student = new Student(1001, "sam", 1, new Score(78, 91)); Student student1 = mapper.map(student, Student.class); System.out.println(student == student1); student1.getScore().setChinese(99); System.out.println(student.toString()); }
結果:可以實現深拷貝
false Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))
參考:
https://blog.csdn.net/54powerman/article/details/64920431?locationNum=6&fps=1
https://blog.csdn.net/weixin_40581980/article/details/81388557