当前位置: 首页 > news >正文

【Java】:浅克隆和深克隆

克隆

克隆和赋值

  • 克隆的结果是有多个相同的实体,各个对象指向不同的实体
  • 而多个不同对象指向一个相同的实体不是克隆,而是赋值

克隆的过程


  1. 首先实例化一个 student1 对象
    • 在堆里开辟了一块内存用来存储 age = 10 这个数据
  2. 调用 clone 方法
    • 在堆中又开辟了一块内存储存和 student1 指向内存区域一模一样的内容
  3. 将克隆后的数据内存地址赋值给 student2
    • student2 指向新克隆出来的区域

image.png|300

  • 但若想访问 clone 方法,需要满足:
    1. 重写 clone 方法,返回 super.clone()
      • 虽然 Object 类是所有类的父类,但它内部的 clone 方法是 protected 修饰的,只有在同包中才能访问,所以需要在新包中重写 clone 方法
      • return super.clone() 的意思就是调用父类(Object类)中的 clone 方法
    2. 强制类型转换调用 clone 方法的对象
      • 调用 clone 方法后的返回类型是 Object类,而调用 clone 方法的对象是 Student
      • 父类Object 需要强制类型转换成子类Student
    3. 需要抛出 CloneNotSuppertedException 异常
    4. 自定义类型必须实现 Clonable 接口
      • 这个接口中没有任何抽象方法
      • 此时这个接口叫做空接口/标记接口
      • 只有实现了这个克隆接口,才具备了可以被克隆的能力

代码

class Student implements Cloneable{public int age = 10;@Override   //重写clone方法protected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student();Student student2 = (Student)student1.clone();System.out.println(student1.age);    //输出:10System.out.println(student2.age);    //输出:10}
}

浅克隆/浅拷贝


  • 浅克隆创建一个新的对象,但只复制原始对象的基本数据类型的字段或引用(地址),而不赋值引用指向的对象
  • 这意味着新对象和原始对象中的引用指向相同的对象
  • 如果对原始对象的引用类型属性进行修改,浅克隆的对象也会受到影响,因为它们引用了相同的堆内存

浅克隆的实现

class Money {public double money = 12.5;
}class Student implements Cloneable{public int age = 10;public Money m = new Money();@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student();Student student2 = (Student)student1.clone();System.out.println(student1.m.money);  //输出:12.5System.out.println(student2.m.money);  //输出:12.5System.out.println("=========");student1.m.money = 100;System.out.println(student1.m.money);  //输出:100System.out.println(student2.m.money);  //输出:100}
}

观察输出结果可以发现:

  • 将 student1 指向的对象m中的 money 成员改变后,student2 指向的对象中的 money 成员的值也变成了相同的值

所以可知:

  • student1 和 student2 指向的 m 对象所指向的 money 成员是一样的
    image.png|466

深拷贝/深克隆

若想在当 student1 修改 money 的值的时候,student2 中的 money 的值不变,就需要使用深克隆了


  • 深克隆创建一个新的对象,并且递归地复制原始对象的所有字段和引用指向的对象,而不仅仅是复制引用本身
  • 深克隆会确保新对象和原始对象之间的所有关系都是独立的
  • 这意味着对新对象所做的修改不会影响到原始对象,因为他们拥有彼此独立的副本

深克隆的实现


  • 深克隆的实现过程image.png|559
    1. 首先实例化一个 student1 对象
      • 在堆里开辟了一块内存用来存储 age = 10m 这两个数据
        • m 是指向 Money 对象的指针或引用,这个 Money 对象里面有一个 money = 12.5 字段
    2. 调用clone 方法
      • 在堆中又开辟了一块内存,存储和 student1 一模一样的内容
    3. 将克隆后的数据所在堆中的地址赋值给 tmp
      • tmp 指向新克隆出来的内容的地址
    4. 克隆实例化的对象 Money
      • Money 类要支持 clone 方法
        • 要实现 Clonable 接口
        • 重写 clone 方法
    5. 将克隆出来的对象的地址赋值给 tmp 中的 m
      • 至此,两个对象分别指向不同的两个内容相同的实体
      • 实例中的引用变量间的修改也互不影响
    6. 返回 tmp
      • tmp 的指向地址赋值给 student2,克隆完成

class Money implements Cloneable{public double money = 12.5;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Student implements Cloneable{public int age = 10;public Money m = new Money();@Overrideprotected Object clone() throws CloneNotSupportedException {Student tmp = (Student) super.clone();tmp.m = (Money)this.m.clone();return tmp;}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student();Student student2 = (Student)student1.clone();System.out.println(student1.m.money);  //输出:12.5System.out.println(student2.m.money);  //输出:12.5System.out.println("=========");student1.m.money = 100;System.out.println(student1.m.money);  //输出:100System.out.println(student2.m.money);  //输出:12.5}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java设计模式的7个设计原则
  • [计算机基础]一、计算机组成原理
  • 在 Windows 上开发.NET MAUI 应用_1.安装开发环境
  • Excel办公技巧:制作二级联动下拉菜单
  • Python excel知识库批量模糊匹配的3种方法实例(fuzzywuzzy\Gensim)
  • 开启你的 Django 开发之旅:从环境搭建到服务部署
  • 保障低压设备安全!中国星坤连接器精密工艺解析!
  • tomcat如何进行调优?
  • 【论文阅读】MCTformer+:弱监督语义分割的多类令牌转换器
  • [C/C++入门][ifelse]20、闰年判断
  • 深度学习,人工智能
  • 探索Java设计模式:构建高效、可维护的软件架构
  • 树莓派docker自制镜像
  • Java | Leetcode Java题解之第238题除自身以外数组的乘积
  • <数据集>钢铁缺陷检测数据集<目标检测>
  • 2018一半小结一波
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • canvas 五子棋游戏
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • ES6--对象的扩展
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • php ci框架整合银盛支付
  • PHP变量
  • Python进阶细节
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • 飞驰在Mesos的涡轮引擎上
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 前嗅ForeSpider教程:创建模板
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 微信公众号开发小记——5.python微信红包
  • 我看到的前端
  • RDS-Mysql 物理备份恢复到本地数据库上
  • ​2021半年盘点,不想你错过的重磅新书
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • $nextTick的使用场景介绍
  • (26)4.7 字符函数和字符串函数
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (ZT)出版业改革:该死的死,该生的生
  • (第一天)包装对象、作用域、创建对象
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (三)elasticsearch 源码之启动流程分析
  • (算法)大数的进制转换
  • (学习日记)2024.02.29:UCOSIII第二节
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • .NET : 在VS2008中计算代码度量值
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .NET Core 实现 Redis 批量查询指定格式的Key