如果饿了就吃,困了就睡,渴了就喝,人生就太无趣了
作者:可耳(keer)
更新时间 : 2020年04月01日
源码地址:https://github.com/keer123456789/java_study_demo


1.概念

1.1 浅拷贝(浅复制、浅克隆):

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象。
如图1:对象obj1经过浅克隆生成对象obj2,但是二者都指向相同地址,这样只要修改任意一个对象值,另一对象也会改变

1.2 深拷贝(深复制、深克隆):

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍
如图2:对象obj1经过深克隆生成对象obj2,此时obj2obj1指向不同的内存地址,修改任何一方,都不会引起另一方的改变。

2.不带属性对象

2.1 浅拷贝

public class User {private String num;public String getNum() {return num;}public void setNum(String num) {this.num = num;}public static void main(String[] args) {User user1=new User();user1.setNum("123456");User user2= user1;User user3=new User();user3.setNum("123456");System.out.println("克隆后,未做修改前,两用户的信息:");System.out.println("User1.num:"+user1.getNum());System.out.println("User2.num:"+user2.getNum());System.out.println("User1和User2是否相等:"+user1.equals(user2));user2.setNum("7890");System.out.println("克隆后,修改User2.num=:"+user2.getNum()+",两用户信息如下:");System.out.println("User1.num:"+user1.getNum());System.out.println("User2.num:"+user2.getNum());System.out.println("User1和User2是否相等:"+user1.equals(user2));}
}

运行结果:

观察debug运行时的内存地址(@后面是该变量的相对内存地址):
可以看到user2是经过user1浅克隆的,二者都指向同一个内存地址,user3是单独的User对象的实例,虽然具有相同的值,但是user3有单独的内存地址。

2.2 深拷贝

public class User implements Cloneable {private String num;public String getNum() {return num;}public void setNum(String num) {this.num = num;}@Overrideprotected User clone() throws CloneNotSupportedException {return (User) super.clone();}public static void main(String[] args) throws CloneNotSupportedException {User user1=new User();user1.setNum("123456");User user2= user1.clone();System.out.println("克隆后,未做修改前,两用户的信息:");System.out.println("User1.num:"+user1.getNum());System.out.println("User2.num:"+user2.getNum());System.out.println("User1和User2是否相等:"+user1.equals(user2));user2.setNum("7890");System.out.println("克隆后,修改User2.num=:"+user2.getNum()+",两用户信息如下:");System.out.println("User1.num:"+user1.getNum());System.out.println("User2.num:"+user2.getNum());System.out.println("User1和User2是否相等:"+user1.equals(user2));}
}

运行结果:
可以看到对user2进行修改,没有对user1产生影响。

观察debug运行时的内存地址:此时内存地址都是不同的。

3.带有属性对象

3.1 浅拷贝(属性对象没有实现Cloneable接口)

public class Address {private String add;public String getAdd() {return add;}public void setAdd(String add) {this.add = add;}
}public class Student implements Cloneable {private int number;private Address addr;public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Overridepublic Object clone() {Student stu = null;try {stu = (Student) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return stu;}public static void main(String[] args) {Address addr = new Address();addr.setAdd("杭州市");Student stu1 = new Student();stu1.setNumber(123);stu1.setAddr(addr);Student stu2 = (Student) stu1.clone();System.out.println("克隆后,未做修改前,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:"+stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:"+stu1.getAddr().equals(stu2.getAddr()));stu1.setNumber(3333);stu1.getAddr().setAdd("中国");System.out.println("克隆后,修改stu1后,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:"+stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:"+stu1.getAddr().equals(stu2.getAddr()));}
}

运行结果:


修改其中一个学生的信息,两个学生的地址信息会随着改变而变化,但是学号不会改变。看一下运行时的内存情况:

在修改学生信息前:

  • 两个学生的实例的内存地址是不同的
  • 由于地址对象没有实现Cloneable接口,所以每个学生对象的实例的地址属性的内存地址都指向同一个地址。

在修改学生信息后:

  • 地址的变化会随着改变而导致所有的学生实例中的地址属性也发生变化。
  • 一个实例的学号改变,不会影响其他人的信息。

3.2 浅拷贝(属性对象实现Cloneable接口,但未显示的调用)

public class Address2 implements Cloneable{private String add;public String getAdd() {return add;}public void setAdd(String add) {this.add = add;}@Overridepublic Object clone() {Address2 address = null;try {address = (Address2) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return address;}}public class Student2 implements Cloneable{private int number;private Address2 addr;public Address2 getAddr() {return addr;}public void setAddr(Address2 addr) {this.addr = addr;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Overridepublic Object clone() {Student2 stu = null;try {stu = (Student2) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return stu;}public static void main(String[] args) {Address2 addr = new Address2();addr.setAdd("杭州市");Student2 stu1 = new Student2();stu1.setNumber(123);stu1.setAddr(addr);Student2 stu2 = (Student2) stu1.clone();System.out.println("克隆后,未做修改前,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:"+stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:"+stu1.getAddr().equals(stu2.getAddr()));stu1.setNumber(3333);stu1.getAddr().setAdd("中国");System.out.println("克隆后,修改stu1后,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:"+stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:"+stu1.getAddr().equals(stu2.getAddr()));}
}

运行结果:

和之前的相似,地址属性依旧没有进行克隆,只是将学生进行了浅拷贝。

运行时内存的分布:

  • 两个学生实例的地址属性都指向同一个地址。

3.3 深拷贝(属性对象实现Cloneable接口,显示调用)

public class Address implements Cloneable{private String add;public String getAdd() {return add;}public void setAdd(String add) {this.add = add;}@Overridepublic Object clone() {Address address = null;try {address = (Address) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return address;}
}public class Student implements Cloneable {private int number;private Address addr;public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Overridepublic Object clone() {Student stu = null;try {stu = (Student) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}// 对象属性设置stu.addr = (Address) addr.clone();return stu;}public static void main(String[] args) {Address addr = new Address();addr.setAdd("北京市");Student stu1 = new Student();stu1.setNumber(123);stu1.setAddr(addr);Student stu2 = (Student) stu1.clone();System.out.println("克隆后,未做修改前,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:" + stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:" + stu1.getAddr().equals(stu2.getAddr()));stu1.setNumber(3333);stu1.getAddr().setAdd("中国");System.out.println("克隆后,修改stu1后,学生的信息:");System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println("stu1 和stu2是否相等:" + stu1.equals(stu2));System.out.println("stu1 和stu2的地址是否相等:" + stu1.getAddr().equals(stu2.getAddr()));}
}

运行结果:


此次克隆过后,两个学生实例的地址属性不是相等的,修改其中一个学生的地址属性,不会引起其实例的地址属性变化。

运行时内存的分布:

两个学生的地址属性的所在的内存地址是不同的。

参考文章:

https://blog.csdn.net/dengjili/article/details/85716058

【Java学习】Clone 分析相关推荐

  1. Java学习---RMI 技术分析[Hessian]

    一.什么是Hessian Hessian 是一个基于 binary-RPC 实现的远程通讯 library.使用二进制传输数据.Hessian通常通过Web应用来提供服务,通过接口暴露.Servlet ...

  2. Java入门1.2.3—一个老鸟的Java学习心得

    Java入门1.2.3-一个老鸟的Java学习心得 基本信息 作者: 臧萌    出版社:清华大学出版社 ISBN:9787302217831 上架时间:2010-3-30 出版日期:2010 年3月 ...

  3. POCO C++库学习和分析 -- 序

    POCO C++库学习和分析 -- 序 1. POCO库概述: POCO是一个C++的开源库集.同一般的C++库相比,POCO的特点是提供了整一个应用框架.如果要做C++程序应用框架的快速开发,我觉得 ...

  4. java TreeMap 源代码分析 平衡二叉树

    TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 Tre ...

  5. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  6. 透露|Java学习的最后一点小秘密

    2019独角兽企业重金招聘Python工程师标准>>> 版权声明:本文为北京尚学堂原创文章,未经允许不得转载. 好久了,都想向学习JAVA的新人写点东西,因为我实在看不下去了,看不下 ...

  7. java学习笔记16--I/O流和文件

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址. IO(Input  Output)流 IO流用来处理 ...

  8. java学习笔记9--内部类总结

    java学习笔记系列: java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对 ...

  9. java学习笔记8--接口总结

    接着前面的学习: java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对象的初始化与回收 java学习笔记3- ...

  10. java学习笔记6--类的继承、Object类

    接着前面的学习: java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) java学习笔记2--数据类型.数组 java学习笔记 ...

最新文章

  1. android sdk国内快速更新下载
  2. git clone从远程主机克隆一个版本库
  3. 开发奇淫巧技Tips(Android篇)
  4. SpringBoot最佳实践-Lombok简化开发
  5. RHEL5.4在线调整磁盘分区大小
  6. C++之virtual 方法
  7. 使用Office Word 2010/2013 发布文章到博客园
  8. amCharts: JavaScript/HTML5 charts 破解
  9. 自动化机器人 rpa_机器人过程自动化和机器人的出现
  10. cknife连接失败
  11. matlab 函数教程,MATLAB函数 - Matlab教程
  12. 假设检验、显著性水平α、P值、置信区间
  13. 好看视频出击,从Q2财报看百度的短视频谋局
  14. 百度开放大数据平台接口,传统企业看到了新曙光。
  15. 阿里员工调侃:花名考拉的妹子离职去了网易考拉,现在又回来了
  16. 搭档之家:李佳琦“双11”直播最低价,还是贵了?
  17. epics安装css,EPICS-synApps/areaDetector安装
  18. 并发线程和线程间通信(event、mailbox和semaphore)-systemVerilog
  19. 人工智能前沿——AI技术在医疗领域的应用(二)
  20. 黄浩老师cpp平时作业(二)蒙特卡罗阴影面积

热门文章

  1. 门门通还是精通一门(程序员)
  2. 华为鸿蒙跑了个“hello world”!跑通后,我特么开始怀疑人生....
  3. Detecting Spacecraft Anomalies Using LSTMs and Nonparametric Dynamic Thresholding阅读笔记
  4. Pytorch中的 AdaptivePooling
  5. 用计算机研究心理,计算机使用中的自我效能感
  6. Error response from daemon: driver failed programming external connectivity on endpoint xenodochial_
  7. Tex_开题报告beamer模板
  8. 分布式架构中的八大谬误
  9. 医学信息化管理与建设
  10. 华为OD机试 - 乱序整数序列两数之和绝对值最小