深拷贝和浅拷贝java_Java基础(十三)--深拷贝和浅拷贝

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

org.modelmapper

modelmapper

1.1.0

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依赖

commons-beanutils

commons-beanutils

1.8.0

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依赖

cglib

cglib-nodep

3.1

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实现对象拷贝:

ma.glasnost.orika

orika-core

1.5.0

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))

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值