基于java实现浅拷贝和深拷贝
目录
- 1、概念
- 2、浅拷贝
- 2.1、浅拷贝实战
- 3、深拷贝
- 3.1、嵌套 clone 方法
- 3.2、使用序列化流
- 3.3、使用开源工具类
1、概念
浅拷贝:在拷贝一个对象时,复制基本数据类型的成员变量,但对引用数据类型的成员变量只进行引用的传递(复制其地址引用),并不会创建一个新的对象。简单地说就是被拷贝对象和浅拷贝得到的新对象,它们的引用数据类型的成员变量指向同一个内存地址。
深拷贝:在拷贝一个对象时,除了复制基本数据类型的成员变量,对引用数据类型的成员变量进行拷贝时,会创建一个新的对象来保存引用类型的成员变量。
简单地说就是被拷贝对象和深拷贝得到的新对象,它们的引用数据类型的成员变量指向不同的内存地址。
定义如下类:
class Person {private int age;private StringBuffer name;
}
对 Person 类的对象拷贝时,浅拷贝和深拷贝示意图区别如下:
2、浅拷贝
Java已经内置了 Cloneable 抽象原型接口,自定义的类型只需实现该接口并重写 Object.clone() 方法即可完成本类的浅拷贝。
Cloneable 是一个空接口。Java之所以提供 Cloneable 接口,只是为了在运行时通知Java虚拟机可以安全地在该类上使用 clone() 方法。而如果该类没有实现 Cloneable 接口,则调用 clone() 方法会抛出 CloneNotSupportedException 异常。
一般情况下,如果使用 clone() 方法,则需满足以下条件:
1、对任何对象 o,都有 o.clone() != o。换言之,克隆对象与原型对象不是同一个对象;
2、对任何对象 o,都有 o.clone().getClass() == o.getClass()。换言之,复制对象与原对象的类型一样;
3、如果对象 o 的 equals() 方法定义恰当,则 o.clone().equals(o) 应当成立。
2.1、浅拷贝实战
假设,现在有两个类,学生 Student 和班级 Class,每个学生都有自己对应的班级:
/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class {private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Cloneable{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;/*** 重写 Object.clone()方法*/@Overridepublic Student clone() throws CloneNotSupportedException {/*** super.clone()方法直接从堆内存中以二进制流的方式进行复制,重新分配一个内存块,其效率很高;* 由于super.clone()方法基于内存复制,不会调用对象的构造函数,也就是不需要经历初始化过程;* 使用super.clone()方法,如果类中存在引用对象属性,则原型对象与克隆对象的该属性会指向同一对象的引用。*/return (Student)super.clone();}
}
浅拷贝测试:
/*** @Des 浅拷贝测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级", "一年级的学生比较调皮");Student student = new Student(1, "基地", 7, classs);// 浅拷贝对象Student clone = student.clone();// 结果为 true,指向的是同一个内存地址System.out.println(student.getClasss() == clone.getClasss());}
}
3、深拷贝
深拷贝可以有以下几种不同的实现方式:
1、嵌套 clone 方法:在需要克隆的对象以及该对象的引用类型的变量的类中全部实现 cloneable 接口,但是对于层级比较深的对象,不太友好;
2、使用序列化流:使要序列化的对象和该对象的引用类型成员变量对象的类都实现 Serializable 接口,将对象序列化到输出流中,然后再反序列化为对象就完成了完全的复制操作。但是静态的成员和 transient 关键字修饰的成员不能被序列化;
3、使用开源工具类,例如,json类库(FastJson,GSON等将对象转化为json字符串,然后将json字符串转换为对象),Spring的BeanUtils,Cglib的BeanCopier。
3.1、嵌套 clone 方法
定义实体类 Student 和 Class,两个实体类都实现 Cloneable 接口并重写 Object.clone(),对于引用数据类型的变量继续使用其重写的 clone 方法进行拷贝:
/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class implements Cloneable{private int id;private String name;private String desc;/*** 重写 Object.clone()方法*/@Overridepublic Class clone() throws CloneNotSupportedException {return (Class)super.clone();}
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Cloneable{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;/*** 重写 Object.clone()方法*/@Overridepublic Student clone() throws CloneNotSupportedException {Student student = (Student)super.clone();/*** 对于引用数据类型,继续调用clone方法* 如果引用数据类型嵌套层级很多,每个层级都需要处理*/student.classs = this.classs.clone();return student;}
}
测试:
/*** @Des 深拷贝测试(嵌套 clone)* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 深拷贝对象Student clone = student.clone();// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}
3.2、使用序列化流
定义序列化工具类 SerialCloneUtils :
/*** @Des* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/12*/
@Slf4j
public class SerialCloneUtils {/*** @Desc 使用ObjectStream序列化实现深克隆* @Author jidi* @date 2022/7/12 9:52*/public static <T extends Serializable> T deepClone(T t) throws CloneNotSupportedException {ByteArrayOutputStream bout = null;ObjectOutputStream out = null;InputStream bin = null;ObjectInputStream in = null;try {// 保存对象为字节数组bout = new ByteArrayOutputStream();out = new ObjectOutputStream(bout);out.writeObject(t);// 从字节数组中读取克隆对象bin = new ByteArrayInputStream(bout.toByteArray());in = new ObjectInputStream(bin);return (T)(in.readObject());}catch (IOException | ClassNotFoundException e){CloneNotSupportedException cloneNotSupportedException = new CloneNotSupportedException();e.initCause(cloneNotSupportedException);throw cloneNotSupportedException;}finally {try {if (Objects.nonNull(bout)){bout.close();}if (Objects.nonNull(out)){out.close();}if (Objects.nonNull(bin)){bin.close();}if (Objects.nonNull(in)){in.close();}} catch (IOException e) {log.error("关闭IO流失败:{}", e);}}}
}
创建一个公共父类 SerialClone ,只要继承该类就可以实现深克隆:
/*** @Des* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/12*/
public class SerialClone implements Cloneable, Serializable {private static final long serialVersionUID = -3556726131986995463L;@Overridepublic Object clone() throws CloneNotSupportedException {return SerialCloneUtils.deepClone(this);}}
定义实体类 Student 和 Class,分别继承 SerialClone 类:
/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class extends SerialClone{private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student extends SerialClone{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;
}
测试:
/*** @Des 深复制测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 深拷贝对象Student clone = (Student)student.clone();// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}
3.3、使用开源工具类
此处使用 FastJson 实现深拷贝:
/*** @Des 班级实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Class{private int id;private String name;private String desc;
}/*** @Des 学生实体类* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{private int id;private String name;private int age;/*** 班级,一对一关系*/private Class classs;
}/*** @Des 深复制测试* @Author jidi* @Email jidi_jidi@163.com* @Date 2022/7/11*/
public class StudentTest {public static void main(String[] args) throws CloneNotSupportedException {Class classs = new Class(1, "一年级","一年级的学生比较调皮");Student student = new Student(1, "基地",7, classs);// 使用 FastJson 深拷贝对象Student clone = JSONObject.parseObject(JSONObject.toJSONBytes(student), Student.class);// 结果为falseSystem.out.println(student.getClasss() == clone.getClasss());}
}
可以看到使用JSON类库进行深拷贝时,实体类不需要实现任何接口即可。但是需要注意的是,不同的JSON类库序列化规则不一样,对于某些表示是否的变量,命名时最好不要使用 isXxx 形式,否则会导致序列化失败。
基于java实现浅拷贝和深拷贝相关推荐
- Java的浅拷贝与深拷贝总结
Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.举例说明:比如,对象A和对象B都属于类S,具有属性a和b.那么对对象A进行拷贝 ...
- java中浅拷贝和深拷贝_java中的浅拷贝和深拷贝
复制 将一个对象的引用复制给另一个对象,一共有三种方式.第一种方式是直接赋值,第二种方式是浅复制,第三种方式是深复制. 1.直接赋值 在Java中,A a1 = a2,这实际上复制的是引用,也就是说 ...
- Java中浅拷贝与深拷贝之间的区别
在深入探讨Java中浅表副本与深表副本之间的差异之前,让我们看看首先进行克隆的是什么. 什么是克隆? 克隆是在内存中创建现有对象的精确副本的过程.在Java中,java.lang.Object类的cl ...
- Java中浅拷贝和深拷贝的区别
深拷贝和浅拷贝的区别 浅拷贝:被拷贝的对象的所有属性值都与原来的对象相同,而对象的所有属性引用仍然指向原来的属性所指向的内存地址.需要注意的是cloneObj == obj 返回的是false,所以使 ...
- Java【浅拷贝和深拷贝】之间的区别
浅拷贝 浅拷贝定义定义 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.即对象的浅拷贝会对"主"对象进行拷贝,但不会复制主对象里面所引用 ...
- Java提高篇 —— Java浅拷贝和深拷贝
一.前言 我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能 ...
- Java浅拷贝和深拷贝的方式
文章目录 1. 前言 2. 概念介绍 2.1 拷贝 / 克隆的概念 2.2 为什么需要拷贝方法? 2.3 什么是浅拷贝?浅拷贝和深拷贝的区别是什么? 3. 深拷贝的实现方式 3.1 手动深拷贝 3.2 ...
- Java基础—复制之深拷贝与浅拷贝
目录 一.浅拷贝(Shallow Copy) 二.深拷贝(Deep Copy) Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去 ...
- java深入理解浅拷贝和深拷贝
文章目录 简介 拷贝接口 使用clone导致的浅拷贝 使用clone的深拷贝 不要overridden clone 总结 简介 拷贝对象是java中经常会遇到的问题.java中存在两种类型,基础类型和 ...
最新文章
- win10如何查看NVIDIA驱动的版本
- 使用ContentProvider
- SAP内部订单使用实例
- 在RHEL5/CentOS5上配置使用Open×××
- jquery 里 $(this)的用法
- 转:OAuth 2.0
- c swap方法在哪个库里面_IOT操作系统用C++库的经验总结
- 全球抵押销售点(POS)软件行业调研及趋势分析报告
- loacallhost:80被占用解决方法
- 解决Office2010每次打开都要配置进度的问题
- Java获取时间戳,System.currentTimeMillis() 和 System.nanoTime() 哪个更快?
- 井字游戏HTML,HTML5井字棋游戏
- 使用rufus制作windows系统安装u盘
- 加权算数平均大于等于几何平均
- Spring Boot中的配置文件使用以及重新加载
- 媒体实录:百度林元庆第一时间解读百度大脑VS.最强大脑第一场
- 用计算机弹百战成诗,百战成诗(80P纯女热血翻唱)
- 原根算法C语言,算法导论-----数论-----元素的幂
- 螺旋无限延伸_八卦中的双螺旋结构,无限大∞符号隐含的秘密
- 展望计算机体系结构的未来发展方向(学院作业)随便写的
热门文章
- MLR原理及deepctr组网实现MLR
- Java中OOA、OOD、OOP、OOT、OOSM、OOM的具体含义解释
- AutoFormR8冲压连续模具带料分析视频教程
- “慷慨的上帝” vs “吝啬的上帝”
- 解决Python安装alipay-sdk-python3.3.398遇到的错误
- 《死亡搁浅》叙事分析:点石成金的“迈达斯之触”
- 深耕城市治理场景,百度智能云联合慧联无限推内涝智能检测预警
- Postgresql - Cluster
- 「事件流处理架构」事件流处理的八个趋势
- 520用代码捕获女神芳心