Java的深拷贝和浅拷贝
熟悉C++的朋友对这个话题应该很熟悉,浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象。深拷贝就是两个对象的值相等,但是互相独立。本来想把以前写的一篇文章扩充一下,没想到居然牵扯出很多复杂的问题。本文测试环境是windows xp sp3中文版、NetBeans6.7.1,JDK1.6-update16。这里抛砖引玉,希望大家能提宝贵意见。
首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:
实现 Cloneable接口,因为 Object的 clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。
改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作, Object的 clone方法是 protected权限。
通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
现在,来看一下不同的类型在拷贝过程中的表现:
|
Operator = |
拷贝构造函数 |
clone方法 |
|
预定义非集合类型 |
深拷贝 |
如果支持拷贝构造函数的类型,则是深拷贝 |
不支持 |
|
自定义类型 |
浅拷贝 |
取决于实现 |
取决于实现 |
|
预定义集合类型 |
浅拷贝 |
会逐个调用每个元素的operator=方法 |
|
下面是测试代码,首先测试的是预定义非集合类型的operator =操作:
int x=1;int y=x;y=2;if(x!=y){System.out.println("deep copy");}Integer a=1;Integer b=a;b=2;if(!a.equals(b)){System.out.println("deep copy");}String m="ok";String n=m;n="no";if(!m.equals(n)){System.out.println("deep copy");}
程序运行后,输出三行deep copy,测试结果表明,这三种类型的operator =操作都是深拷贝。由于我没有测试完所有的预定义非集合类型,我这里推测它们的operator =都是深拷贝。
下面测试预定义非集合类型的拷贝构造函数:
Integer a=1;Integer b=new Integer(a);b=2;if(!a.equals(b)){System.out.println("deep copy");}String m="ok";String n=new String(m);n="no";if(!m.equals(n)){System.out.println("deep copy");}
程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。
现在我们来测试自定义类型的operator=操作,假设我有一个类Person,代码如下:
public class Person implements Cloneable{private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}void setName(String name) {this.name = name;}
}
测试代码:
Person p=new Person();p.setAge(32);p.setName("陈抒");Person p2=p;p.setAge(33);p.setName("老陈");if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){System.out.println("deep copy");}
运行后,没有输出deep copy,说明这是浅拷贝。这里就是我们常说的两个引用之间的赋值,仅仅是让两个引用指向同一个对象。
现在,我们来测试预定义集合类型的operator=操作:
ArrayList list1=new ArrayList();list1.add("yangzhou");ArrayList list2=list1;list1.clear();if(list2.isEmpty()){System.out.println("shallow copy");}
结果输出为shallow copy。
现在我来测试拷贝构造函数:
ArrayList list1=new ArrayList();list1.add("yangzhou");ArrayList list2=new ArrayList(list1);list1.clear();if(list2.isEmpty()){System.out.println("shallow copy");}else{System.out.println("deep copy");}
输出结果是deep copy;
clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 。
预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:
ArrayList list1=new ArrayList();Person p1=new Person();p1.setAge(32);p1.setName("陈抒");list1.add(p1);ArrayList list2=(ArrayList) list1.clone();list2.get(0).setName("chenshu");if(list2.get(0).getName().equals(list1.get(0).getName())){System.out.println("shallow copy");}else{System.out.println("deep copy");}
输出为shallow copy,Person是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。
现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。
假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;
假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:
public class Company {public Company(){}public Company(Company c){name=c.name;person=new Person(c.person);}private String name;private Person person;public Person getPerson() {return person;}public void setPerson(Person person) {this.person = person;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic Object clone() throws CloneNotSupportedException{Company c=new Company();c.setName(name);c.setPerson((Person) person.clone());return c;}
}public class Person implements Cloneable{public Person(){}public Person(Person p){age=p.age;name=p.name;}private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic Object clone() throws CloneNotSupportedException{return super.clone();}
}
Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Person的clone方法。
Java的深拷贝和浅拷贝相关推荐
- 学习Java的深拷贝和浅拷贝
关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用 ...
- Java基础-深拷贝和浅拷贝的区别
深拷贝与浅拷贝 一般来说,拷贝的类型分为 深拷贝与浅拷贝. |-----------------------------| | 深拷贝:引用对象的值等信息,复制一份一样的. | ...
- java数组深拷贝和浅拷贝_java List复制:浅拷贝与深拷贝
Java的拷贝可以分为三种:浅拷贝(Shallow Copy).深拷贝(Deep Copy).延迟拷贝(Lazy Copy). 在java中除了基本数据类型之外(int,long,short等),还存 ...
- java:深拷贝与浅拷贝
拷贝的实现: 只有子类实现了Cloneable接口后才可以使用Object类提供的clone方法. protected native Object clone() throws CloneNotSup ...
- java:clone 深拷贝与浅拷贝,为什么要慎用浅拷贝
转自:https://blog.csdn.net/qq_34110755/article/details/79914639 1.浅拷贝 对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此 ...
- 【Java】深拷贝和浅拷贝,Cloneable接口
活动地址:CSDN21天学习挑战赛 ✨博客主页: XIN-XIANG荣 ✨系列专栏:[Java SE] ✨一句短话: 难在坚持,贵在坚持,成在坚持! 文章目录 1. Cloneable接口的介绍 2. ...
- 深入理解java 的深拷贝和浅拷贝
一 深拷贝和钱拷贝的概念 1.深拷贝 将所有的引用对象拷贝.例如a引用b,b引用c,在拷贝得到a'时,a'里面的引用对象是b',b'里面的引用对象是c'. 直接输入a和a',看到他们是不同的引用地址. ...
- java数组深拷贝和浅拷贝_java中的深拷贝与浅拷贝(值类型 vs 引用类型)
对象赋值 赋值是日常编程过程中最常见的操作,最简单的比如: Student codeSheep = new Student(); Student codePig = codeSheep; 严格来说,这 ...
- JAVA中深拷贝与浅拷贝(在网上找到的) 希望对于理解深拷贝与浅拷贝有帮助...
什么是clone? 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就 ...
最新文章
- doker zookeeper kafka单机搭建
- 蜕变与成长中的青春创作:评论家谈少数民族青年作家的创作
- linux inputuevent使用
- 使用Tab Bar切换视图
- 演练 鼠划图片上变亮的效果 1022
- android java 指针异常处理,Android自定义抛出异常的方法详解
- C语言union类型和C语言 uchar类型的个人见解
- 没有com.sun.tools.javac.main的解决办法
- 形式语言与自动机之一 语言与文法
- JAVA最强工具类之一HuTool
- php 去除单引号,php如何去除双引号
- 某网站cookie加密黑盒调用与算法还原
- 0.《JavaScript高级程序设计》(Nicholas C.Zakas 第3版)
- 什么是url,herf和src的区别
- Leetcode 1125:最小的必要团队
- N条线段求交的扫描线算法
- 微信小程序--放入个性化手绘地图具体步骤(腾讯地图)
- 为什么要有不同的参考文献格式?
- oracle账户别名,Oracle的别名
- 简单的一些网络流问题