一、简介

在Object基类中,有一个方法叫clone,产生一个原始对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝一份,而浅克隆仅仅是拷贝一份此属性的引用。

  • clone()方法是Object类的,并不是Cloneable接口的,Cloneable只是一个标记接口,标记接口是用用户标记实现该接口的类具有某种该接口标记的功能,没有实现Cloneable接口,那么调用clone方法就会报错CloneNotSupportedException异常。
  • Object类中的clone方法是protected修饰的,这就表明我们在子类中不重写此方法,就在子类外无法访问,因为这个protected权限是仅仅能在Object所在的包和子类能访问的,这也验证了子类重写父类方法权限修饰符可以变大但不能变小的说法。
protected native Object clone() throws CloneNotSupportedException;@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

二、浅克隆

浅克隆就是引用类型的属性无法完全复制,类User中包含成绩属性Mark,Mark是由Chinese和math等等组成的,浅克隆示例:

/*** @Description: 教师类* @author: weishihuai* @Date: 2019/10/22 20:10*/
public class Teacher {private Integer pkid;private String name;public Teacher(Integer pkid, String name) {this.pkid = pkid;this.name = name;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Teacher{" +"pkid='" + pkid + '\'' +", name='" + name + '\'' +'}';}
}package com.wsh.springboot.springbootdesignpattern.prototypepattern.shallowclone;/*** @Description:* @author: weishihuai* @Date: 2019/10/22 20:11*/
public class Student implements Cloneable {private double weight;private Integer pkid;private String name;private Teacher teacher;public Student(double weight, Integer pkid, String name, Teacher teacher) {this.weight = weight;this.pkid = pkid;this.name = name;this.teacher = teacher;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@Overrideprotected Object clone() {Object object = null;try {object = super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();System.out.println(e.getMessage());}return object;}@Overridepublic String toString() {return "Student{" +"weight=" + weight +", pkid=" + pkid +", name='" + name + '\'' +", teacher=" + teacher +'}';}
}/*** @Description: 客户端* @author: weishihuai* @Date: 2019/10/22 20:13*/
public class Client {public static void main(String[] args) {Teacher teacher = new Teacher(1, "黄老师");Student student = new Student(50.0D, 1, "张三", teacher);Student student1 = (Student) student.clone();System.out.println(student == student1);System.out.println(student.getTeacher() == student1.getTeacher());System.out.println("复制前teacher的hashCode: " + student.getTeacher().hashCode());System.out.println("复制之后teacher新对象的hashCode: " + student1.getTeacher().hashCode());System.out.println("克隆之前student学生的老师: " + student.getTeacher().getName());//克隆对象student1将老师姓名修改为张老师,导致旧student的老师姓名也被修改。Teacher teacher1 = student1.getTeacher();teacher1.setName("张老师");System.out.println("克隆之后student学生的老师: " + student.getTeacher().getName());System.out.println("克隆之后的新学生的老师: " + student.getTeacher().getName());}
}

输出结果:

可见浅拷贝之后的student对象与原来的对象是不同的对象,但是student对象里面的teacher对象与原teacher还是指向的同一个地址,旧对象修改会导致新对象也被修改,这就是浅拷贝的缺点,浅拷贝在克隆基本数据类型的时候比较常见。

特殊情况:属性是String的情况,String也是一个类,那String是引用类型吗?String的表现有的像基本类型,归根到底就是因为String不可变性,克隆之后两个引用指向同一个String,但当修改其中的一个,改的不是String的值,却是新生成一个字符串,让被修改的引用指向新的字符串。外表看起来就像基本类型一样。

三、深克隆

  • 方式一:clone函数的嵌套调用
public class Student implements Cloneable {private double weight;private Integer pkid;private String name;private Teacher teacher;public Student(double weight, Integer pkid, String name, Teacher teacher) {this.weight = weight;this.pkid = pkid;this.name = name;this.teacher = teacher;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@Overrideprotected Object clone() {Student student = null;try {student = (Student) super.clone();student.teacher = (Teacher) this.teacher.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();System.out.println(e.getMessage());}return student;}@Overridepublic String toString() {return "Student{" +"weight=" + weight +", pkid=" + pkid +", name='" + name + '\'' +", teacher=" + teacher +'}';}
}/*** @Description: 教师类* @author: weishihuai* @Date: 2019/10/22 20:10*/
public class Teacher implements Cloneable {private Integer pkid;private String name;public Teacher(Integer pkid, String name) {this.pkid = pkid;this.name = name;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Teacher{" +"pkid='" + pkid + '\'' +", name='" + name + '\'' +'}';}@Overrideprotected Object clone() {Object object = null;try {object = super.clone();} catch (CloneNotSupportedException e) {System.out.println(e.getMessage());e.printStackTrace();}return object;}
}/*** @Description: 客户端* @author: weishihuai* @Date: 2019/10/22 20:13*/
public class Client {public static void main(String[] args) {Teacher teacher = new Teacher(1, "黄老师");Student student = new Student(50.0D, 1, "张三", teacher);Student student1 = (Student) student.clone();System.out.println(student);System.out.println(student1);System.out.println(student == student1);System.out.println(student.getTeacher() == student1.getTeacher());System.out.println("复制前teacher的hashCode: " + student.getTeacher().hashCode());System.out.println("复制之后teacher新对象的hashCode: " + student1.getTeacher().hashCode());}
}

可见,复制之后的student对象以及对应的teacher对象都是全新的,与旧对象互不影响,这就是深克隆。深拷贝关键点在于,实现cloneable接口以及用object的clone方法。

  • 方式二:序列化

序列化方式只需要给每个类都实现一个Serializable接口,也是标记接口,最后同序列化和反序列化操作达到克隆的目的(包括数组的复制):

import java.io.Serializable;/*** @Description: 教师类* @author: weishihuai* @Date: 2019/10/22 20:10*/
public class Teacher implements Serializable {private Integer pkid;private String name;public Teacher(Integer pkid, String name) {this.pkid = pkid;this.name = name;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Teacher{" +"pkid='" + pkid + '\'' +", name='" + name + '\'' +'}';}}import java.io.*;/*** @Description:* @author: weishihuai* @Date: 2019/10/22 20:11*/
public class Student implements Serializable {private double weight;private Integer pkid;private String name;private Teacher teacher;public Student(double weight, Integer pkid, String name, Teacher teacher) {this.weight = weight;this.pkid = pkid;this.name = name;this.teacher = teacher;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public Integer getPkid() {return pkid;}public void setPkid(Integer pkid) {this.pkid = pkid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@Overridepublic String toString() {return "Student{" +"weight=" + weight +", pkid=" + pkid +", name='" + name + '\'' +", teacher=" + teacher +'}';}public Student deepCloneBySerializable() {Student student = null;ByteArrayOutputStream byteArrayOutputStream = null;ObjectOutputStream objectOutputStream = null;ByteArrayInputStream byteArrayInputStream = null;ObjectInputStream objectInputStream = null;try {byteArrayOutputStream = new ByteArrayOutputStream();objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(this);byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());objectInputStream = new ObjectInputStream(byteArrayInputStream);student = (Student) objectInputStream.readObject();return student;} catch (IOException | ClassNotFoundException e) {e.printStackTrace();} finally {if (null != objectInputStream) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}if (null != byteArrayInputStream) {try {byteArrayInputStream.close();} catch (IOException e) {e.printStackTrace();}}if (null != objectOutputStream) {try {objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if (null != byteArrayOutputStream) {try {byteArrayOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}return student;}
}/*** @Description: 客户端* @author: weishihuai* @Date: 2019/10/22 20:13*/
public class Client {public static void main(String[] args) {Teacher teacher = new Teacher(1, "黄老师");Student student = new Student(50.0D, 1, "张三", teacher);Student student1 = (Student) student.deepCloneBySerializable();System.out.println(student);System.out.println(student1);System.out.println(student == student1);System.out.println(student.getTeacher() == student1.getTeacher());System.out.println("复制前teacher的hashCode: " + student.getTeacher().hashCode());System.out.println("复制之后teacher新对象的hashCode: " + student1.getTeacher().hashCode());}
}

这就是使用序列化方式深拷贝对象,推荐使用此种方式实现深拷贝,避免重写clone()方法逻辑太多于复杂。

面试题之浅克隆和深克隆相关推荐

  1. 原型模式——浅克隆和深克隆

    概述 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象. 结构 原型模式包含如下角色: 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法. 具体原型类: ...

  2. Java浅克隆与深克隆区别详解与实现,以及String类型属性克隆为什么不受影响?克隆clone()方法中为什么是super.clone()

    1. 浅克隆实现 public class CloneTest {public static void main(String[] args) throws CloneNotSupportedExce ...

  3. 【重难点】【Java基础 06】浅克隆与深克隆、Object类的常用方法、util包下的接口

    [重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 文章目录 [重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 ...

  4. java 浅克隆_(Java)浅克隆与深克隆的区别

    克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆.浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变.但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内 ...

  5. JS:对象的浅克隆、深克隆

    JS中对象的深浅克隆 一.对象的浅克隆 1.浅克隆:只克隆对象的"表层",如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用 2.使用for-in-循环即可 ...

  6. Java中的浅克隆与深克隆

    Java中的浅克隆与深克隆 一:前言 二:浅克隆与深克隆的区别 一:前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个 ...

  7. Java浅克隆与深克隆-clone

    文章目录 clone 浅克隆和深克隆的区别 浅克隆和深克隆的特点 另一种参考 浅克隆 深克隆 实现 扩展 解决多层克隆问题 总结 clone 浅克隆和深克隆的区别 浅克隆: 对当前对象进行克隆,并克隆 ...

  8. 详细分析Java中的浅克隆和深克隆

    本文对浅克隆和深克隆的两种方法(不引入别的开源工具)进行了简单的代码实现(没有内部类语法),对比了浅克隆和深克隆对引用类型的影响,暂不考虑不可变类,确保初学Java者能够看懂并学会,可直接复制源代码进 ...

  9. 浅克隆与深克隆的区别及特点

    浅克隆与深克隆的区别 1.浅克隆:对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象):但如果被克隆的对象中包含除8中数据类 ...

  10. java中浅克隆与深克隆解析

    文章目录 简介 浅克隆 深克隆 嵌套使用clone()方法 序列化方式 浅克隆和深克隆的比较 简介 所谓克隆,就是指依照已存在的数据,复制出一份一样的数据. java中的克隆有浅克隆和深克隆之分,造成 ...

最新文章

  1. 03-C语言的注释与转义字符
  2. 结队-五子棋游戏-项目进度
  3. 系列笔记 | 深度学习连载(2):梯度下降
  4. web.xml 文件中的配置节的加载顺序
  5. 具有InlfuxDB的Spring Boot和Micrometer第2部分:添加InfluxDB
  6. 使用Spring-hadoop小结
  7. 数列分段`Section II`(洛谷-P1182)
  8. 创建主机地址 (A) DNS 记录
  9. Codeforces Beta Round #10 D. LCIS 动态规划
  10. 完成课件中的动手动脑的或需要验证的相关内容。
  11. 10 个内存引发的大坑,你能躲开几个?
  12. 大工17春计算机文化基础在线测试3,大工17春《计算机文化基础》在线测试3满分答案...
  13. 基于php的微信公众号开发,基于ThinkPHP框架快速构建微信公众号开发框架
  14. 中国最顶级的一批程序员,从首富到首负!
  15. ROS Dst-Nat 后内网不能通过公网IP访问内网服务器解决方法
  16. BTTCOJ 问题 C: 逃离机场 广度优先搜索
  17. clover删除多余引导_clover如何删除无用启动项_常见问题解析,clover
  18. 计算机光驱运行功能,电脑如何设置光驱位第一启动项
  19. 报错 | error ‘App‘ is not defined no-undef
  20. 人体全息图——不看你会后悔一辈子!

热门文章

  1. 查看linux的机器内存大小,linux 查看机器内存方法 (free命令)
  2. java sec_java.security文件
  3. LeetCode--Reverse Integer(整数反转)Python
  4. 30. 与所有单词相关联的字串
  5. 整数与IP地址间的转换
  6. 数据分块算法java_分块查询算法(JAVA)
  7. 32个参数累加_「机械设计教程」滚珠丝杠选型过程中考虑的9个参数
  8. Java 重载和重写
  9. windows系统下使用git出现:warning: LF will be replaced by CRLF in
  10. 【ide】myeclipse项目右键没有configure