在上篇文章: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
