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