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

Java基础(十三)--深拷贝和浅拷贝的更多相关文章

  1. 浅谈Java中的深拷贝和浅拷贝(转载)

    浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: ...

  2. 浅谈Java中的深拷贝和浅拷贝

    转载: 浅谈Java中的深拷贝和浅拷贝 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(bool ...

  3. 内功心法 -- Java中的深拷贝和浅拷贝

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...

  4. Java 轻松理解深拷贝与浅拷贝

    目录 前言 直接赋值 拷贝 浅拷贝 举例 原理 深拷贝 实现: Serializable 实现深拷贝 总结 前言 本文代码中有用到一些注解,主要是Lombok与junit用于简化代码. 主要是看到一堆 ...

  5. java克隆之深拷贝与浅拷贝

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java深拷贝与浅拷贝实际项目中用的不多,但是对于理解Java中值传递,引用传递十分重要,同时个人认为对于理解内存模型也有帮助,况且面试中也是经常问 ...

  6. java中的深拷贝与浅拷贝

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  7. JavaScript基础之--- 深拷贝与浅拷贝

    理解深拷贝和浅拷贝之前,先来看一下JavaScript的数据类型. 1.基本类型和引用类型 //案例1 var num1 = 1, num2 = num1; console.log(num1) con ...

  8. java基础(十七)----- 浅谈Java中的深拷贝和浅拷贝 —— 面试必问

    假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short, ...

  9. Java中的深拷贝和浅拷贝

    1.浅拷贝与深拷贝概念 (1)浅拷贝(浅克隆) 浅拷贝又叫浅复制,将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段(java中8中原始类型)的值被复制到副本中后,在副本中的修改不会影响到源 ...

随机推荐

  1. 数据库sqlite3的使用-Navicat的安装

    一:Navicat Navicat是一款著名的数据库管理软件,支持大部分主流数据库(包括SQLite) 1.Navicat的安装 (1)下载该软件后,先打开该软件 (2)把文件拖入到应用程序拷贝 (3 ...

  2. 并不对劲的bzoj4651:loj2086:uoj222:p1712:[NOI2016]区间

    题目大意 有\(n\)(\(n\leq 5*10^5\))个闭区间\([L_1,R_1],[L_2,R_2],...,[L_n,R_n]\)(\(\forall i\in [1,n],0\leq L_ ...

  3. Burnside&Polya

    以前只是直接用了这两个式子..今天才仔细看了证明..[网上的真是难懂啊 我看的几个博客地址(各有优缺): 其实如果能懂的话 只看博客B就可以了 首先是一些置换群方面的定义和性质  博客A:http:/ ...

  4. ACGAN 论文笔记

    <Conditional Image Synthesis With Auxiliary Classifier GANs> Odena A, Olah C, Shlens J. Condit ...

  5. 将json文件转换成insert语句的sql文件

    引入是要的maven依赖: <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <depend ...

  6. ZOJ5593:Let's Chat(双指针)

    传送门 题意 给出x个a区间和y个b区间,询问a和b交区间的子区间长度为m的个数 分析 类似于双指针,具体见代码 trick 代码 #include <bits/stdc++.h> usi ...

  7. bzoj 1601: [Usaco2008 Oct]灌水【最小生成树】

    挺有意思的思路 如果不能自己打井,那么就是MST裸题了,考虑转换一下,自己打井就相当于连接一口虚拟的井(地下水?),所有井i到这口井的距离是w[i],这样把所有边排个序跑MST即可 #include& ...

  8. 关于自增id 你可能还不知道

    导读:在使用MySQL建表时,我们通常会创建一个自增字段(AUTO_INCREMENT),并以此字段作为主键.本篇文章将以问答的形式讲述关于自增id的一切. 注: 本文所讲的都是基于Innodb存储引 ...

  9. BIOS 和UEFI的区别

    BIOS先要对CPU初始化,然后跳转到BIOS启动处进行POST自检,此过程如有严重错误,则电脑会用不同的报警声音提醒,接下来采用读中断的方式加载各种硬件,完成硬件初始化后进入操作系统启动过程:而UE ...

  10. how-to-fix-fs-re-evaluating-native-module-sources-is-not-supported-graceful

    http://stackoverflow.com/questions/37346512/how-to-fix-fs-re-evaluating-native-module-sources-is-not ...