java中clone方法的理解(深拷贝、浅拷贝)
文章目录
- 前言:
- 知识点一:什么是浅拷贝?
- 知识点二:什么是深拷贝?
- 知识点三、java拷贝(clone)的前提:
- 知识点四:浅拷贝案例:
- 拷贝类:
- 测试类:
- 总结:
- 下面通过画图示意:
- 知识点五:深拷贝案例:
- 结果为:
- 总结:
- 画图说明:
- 知识点六:总结
前言:
java中的clone一直是一个老生常谈的问题,另外关于克隆网上也有很多的写过这方面的问题。
我在这里记录一下我遇到的问题和使用clone的方法。
知识点一:什么是浅拷贝?
我们这里说的浅拷贝是指我们拷贝出来的对象内部的引用类型变量和原来对象内部引用类型变量是同一引用(指向同一对象)。但是我们拷贝出来的对象和新对象不是同一对象。简单来说,新(拷贝产生)、旧(元对象)对象不同,但是内部如果有引用类型的变量,新、旧对象引用的都是同一引用。
知识点二:什么是深拷贝?
深拷贝:全部拷贝原对象的内容,包括内存的引用类型也进行拷贝
知识点三、java拷贝(clone)的前提:
1.首先我们需要知道Object类中一个clone()的方法,并且是protected关键字修饰的本地方法(使用native关键字修饰),我们完成克隆需要重写该方法。
注意:按照惯例重写的时候一个要将protected修饰符修改为public,这是JDK所推荐的做法,但是我测试了一下,
复写的时候不修改为public也是能够完成拷贝的。但是还是推荐写成public。2.我们重写的clone方法一个要实现Cloneable接口。虽然这个接口并没有什么方法,但是必须实现该标志接口。
如果不实现将会在运行期间抛出:CloneNotSupportedException异常3.Object中本地clone()方法,默认是浅拷贝
知识点四:浅拷贝案例:
拷贝类:
package cn.cupcat.java8;public class Person implements Cloneable{private String name;private int age;private int[] ints;public int[] getInts() {return ints;}public Person(String name, int age, int[] ints) {this.name = name;this.age = age;this.ints = ints;}public void setInts(int[] ints) {this.ints = ints;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}/*** 默认实现* */@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}
测试类:
package cn.cupcat.java8;import org.junit.Test;public class CloneTest {@Testpublic void test() throws CloneNotSupportedException {int[] ints = {1,2,3};String name = "zhangxiangyang";int age = 23;Person person = new Person("zhangxiangyang",age,ints);System.out.print("一:克隆前: age = "+ age + "... name = "+ name + " 数组:");for (int i : ints){System.out.print(i + " ");}System.out.println();//拷贝Person clonePerson = (Person) person.clone();int clonePersonAge = clonePerson.getAge();String clonePersonName = clonePerson.getName();int[] ints1 = clonePerson.getInts();System.out.print("二:克隆后: age = "+ clonePersonAge + "... name = "+ clonePersonName + " 数组: ");for (int i : ints1){System.out.print(i + " ");}System.out.println();//修改:ints1[0] = 50;//修饰clonePerson.setName("666666666");age = person.getAge();name = person.getName();System.out.println();System.out.print("三:修改后原对象: age = "+ age + "... name = "+ name + "数组 ");for (int i : ints){System.out.print(i + " ");}System.out.println();System.out.println("四:person == clonePerson ? "+ (person == clonePerson ));}
}
结果为:
一:克隆前: age = 23... name = zhangxiangyang 数组:1 2 3
二:克隆后: age = 23... name = zhangxiangyang 数组: 1 2 3 三:修改后原对象: age = 23... name = zhangxiangyang数组 50 2 3
四:person == clonePerson ? false
总结:
1.通过四输出 person == clonePerson ? false 可以看出,克隆以后的对象已经和以前不是同一对象了。因为其引用是不同的。
2.通过分析一、二可以看出我们的拷贝成功执行了,并且拷贝的数据也都正确
3.通过分析三,我们修改了拷贝后对象的name、intsints1[0] = 50;//修饰clonePerson.setName("666666666");发现原对象中的ints也跟着修改了,因此可以证明拷贝后的对象和原对象的ints数组指向了同一引用。而我们发现同时也修改了拷贝对象name属性,为什么那么原对象中的name属性没有发生改变呢?而且String类型也是引用类型呀? 别急,下面我会画图示意。
4. 通过以上总结,我们得出结论:clone()方法的默认实现是浅拷贝
下面通过画图示意:
拷贝成功后:
修改拷贝对象过后:
ps:如果看不清楚图片,可以在标签窗口打开
知识点五:深拷贝案例:
该类只和浅拷贝在clone方法的实现上有区别,其他地方相同:
package cn.cupcat.java8;public class Person implements Cloneable{private String name;private int age;private int[] ints;public int[] getInts() {return ints;}public Person(String name, int age, int[] ints) {this.name = name;this.age = age;this.ints = ints;}public void setInts(int[] ints) {this.ints = ints;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}/*** 深拷贝* */@Overridepublic Object clone() throws CloneNotSupportedException {Person person = new Person(name,age);int[] ints = new int[this.ints.length];System.arraycopy(this.ints,0,ints,0,ints.length);person.setInts(ints);return person;}
}
该测试类和前拷贝测试类相同:
package cn.cupcat.java8;import org.junit.Test;public class CloneTest {@Testpublic void test() throws CloneNotSupportedException {int[] ints = {1,2,3};String name = "zhangxiangyang";int age = 23;Person person = new Person("zhangxiangyang",age,ints);System.out.print("克隆前: age = "+ age + "... name = "+ name + " 数组:");for (int i : ints){System.out.print(i + " ");}System.out.println();//拷贝Person clonePerson = (Person) person.clone();int clonePersonAge = clonePerson.getAge();String clonePersonName = clonePerson.getName();int[] ints1 = clonePerson.getInts();System.out.print("克隆后: age = "+ clonePersonAge + "... name = "+ clonePersonName + " 数组: ");for (int i : ints1){System.out.print(i + " ");}System.out.println();//修改:ints1[0] = 50;//修饰可控后的对象clonePerson.setName("666666666");age = person.getAge();name = person.getName();System.out.println();System.out.print("修改后原对象: age = "+ age + "... name = "+ name + "数组 ");for (int i : ints){System.out.print(i + " ");}System.out.println();System.out.println("person == clonePerson ? "+ (person == clonePerson ));}
}
结果为:
一:克隆前: age = 23... name = zhangxiangyang 数组:1 2 3
二:克隆后: age = 23... name = zhangxiangyang 数组: 1 2 3 三:修改后原对象: age = 23... name = zhangxiangyang数组 1 2 3
四:person == clonePerson ? false
总结:
1.通过四可以看出完成了拷贝,并且克隆对象和原对象不是同一对象(没有同一引用)
2.通过一、二可以看出完成了拷贝并且数据正确
3.通过三,我们修改克隆以后的对象,打印原对象发现没有影响到原对象的数据,也就是说完成的深拷贝下面使用图解说明一下
画图说明:
拷贝完成后:
修改拷贝后对象后:
知识点六:总结
拷贝也算是我们经常使用的一个方法,但是如果是不明白其中原理的程序员可能还是会入坑的。下面总结几条使用建议:1.一定要实现Cloneable接口2.复写clone()方法,注意:默认是浅拷贝,这里需要将引用类型进行深拷贝处理3.特殊:String类虽然是引用类型,但是是final类,同时也有字符串常量池的存在,不必进行处理
java中clone方法的理解(深拷贝、浅拷贝)相关推荐
- java中clone方法_Java Object clone()方法– Java中的克隆
java中clone方法 Cloning is the process of creating a copy of an Object. Java Object class comes with na ...
- java深度克隆_浅析Java中clone()方法浅克隆与深度克隆
现在Clone已经不是一个新鲜词语了,伴随着"多莉"的产生这个词语确实很"火"过一阵子,在Java中也有这么一个概念,它可以让我们很方便的"制造&qu ...
- Java中join()方法的理解
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程. 比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B. t.join ...
- java中gettext方法_深入理解Java中方法的参数传递机制
形参和实参 我们知道,在Java中定义方法时,是可以定义参数的,比如: public static void main(String[] args){ } 这里的args就是一个字符串数组类型的参数. ...
- 《Android游戏开发详解》——第1章,第1.6节函数(在Java中称为“方法”更好)...
本节书摘来自异步社区<Android游戏开发详解>一书中的第1章,第1.6节函数(在Java中称为"方法"更好),作者 [美]Jonathan S. Harbour,更 ...
- java中main方法前的public static void及其后面的(String[] args)【笔记自用】
为什么Java的main方法必须是public static void? 一. void 如下,像C, C++一样,将返回值类型改为int,再返回一个0,虽然编译通过,但是运行时会报错. 找到一种可理 ...
- 关于java中main方法为什么必须是静态的
Main方法是我们学习Java编程语言时知道的第一个方法,你是否曾经想过为什么main方法是public.static.void的.当然,很多人首先学的是C和C++,但是在Java中main方法与前者 ...
- eclipse折叠if语句块_「03」java中的方法以及控制语句
语句块(有时叫做复合语句),是用花括号扩起的任意数量的简单Java语句.块确定了局部变量的作用域.块中的程序代码,作为一个整体,是要被一起执行的.块可以被嵌套在另一个块中,但是不能在两个嵌套的块内声明 ...
- Java中Map集合如何理解(四)——精简
目录 引言 概念 Map集合实现类 HashMap LinkedHashMap TreeMap 默认排序 自定义排序方式 常用API 遍历Map集合 键找值 键值对 Lambda表达式 结束语 引言 ...
最新文章
- 桌子上有个盘子_日本留学生活:留学生在餐厅刷盘子的传闻,竟然在自己身上上演...
- 五位工程师亲述:AI技术人才如何快速成长?
- 经典排序算法之直接选择排序
- 升级 ServeRADI-8i控制器,使用IBM 3650 9797 老服务器支持2T 硬盘
- LAMP(4)Apach和php结合、Apache默认虚拟主机
- 鼠标右键转圈圈_鼠标右键文件夹出现转圈圈假死机情况
- 病的不轻?教你 2 招,拯救拖延症!
- Java黑皮书课后题第6章:*6.15(金融应用:打印税表)程序清单3-5给出了计算税款的程序。使用下面的方法体编写一个计算税款的方法。使用这个方法编写程序,打印可征税人从50000到60000间隔
- SAP Fiori SSL 和 SAML 2.0 配置文档
- 【ArcGIS遇上Python】栅格影像批量除以10000
- Java中的垃圾回收与对象生命周期
- TCP为何采用三次握手来建立连接,若采用二次握手可以吗
- Multi-thread--Windows和Linux下通用的线程接口
- input内强制保留小数点后两位 位数不足时自动补0
- Bootstrap3 列表元素的样式
- Android中Scrollview、ViewPager冲突问题汇总(已解决)
- 深度学习笔记2:关于LSTM神经网络输入输出的理解
- php将请求转发,PHP中实现请求转发(curl)和请求重定向
- linux无线蓝牙鼠标失效,无线蓝牙鼠标失灵怎么办 无线蓝牙鼠标失灵解决方法【详解】...
- mysql 查看 脏页_MySQL:刷脏页