java中深拷贝和java 数组深浅拷贝贝的区别

java 深拷贝与浅拷贝机制详解
作者:QuinnNorris
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了 java 深拷贝与浅拷贝机制详解的相关资料,需要的朋友可以参考下
&java 深拷贝与浅拷贝机制详解
在Java中,拷贝分为深拷贝和浅拷贝两种。java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。
(一)Object中clone方法
如果我们new出一个新对象,用一个声明去引用它,之后又用另一个声明去引用前一个声明,那么最后的结果是:这两个声明的变量将指向同一个对象,一处被改全部被改。如果我们想创建一个对象的copy,这个copy和对象的各种属性完全相同,而且修改这个copy和原对象毫无关系,那么这个时候我们就要用到clone方法。
import java.util.D
* @author QuinnNorris
* java中的两种拷贝机制
public class Clone {
* @param args
* @throws CloneNotSupportedException
public static void main(String[] args) throws CloneNotSupportedException {
// TODO Auto-generated method stub
ClassA valA = new ClassA(1, "old", new Date());
// 声明一个新的ClassA对象,我们不需要太关注ClassA的功能
ClassA valB = valA;
// 将valA引用的对象赋给valB
valA.setObject("new");
// 更改valA中的值,此时valB也被更改了,因为valA和valB指向同一个对象
valB = valA.clone();//通过clone方法制造副本
ClassA类中关于clone方法的重写部分:
//需要实现Cloneable接口
public class ClassA implements Cloneable {
public ClassA clone() throws CloneNotSupportedException {
return (ClassA) super.clone();//调用父类(Object)的clone方法
1.如何使用Object中clone方法的
有人总结使用clone方法的四条法则,我们一起分享一下:
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
在派生类中覆盖基类的clone()方法,并声明为public。
在派生类的clone()方法中,调用super.clone()。
在派生类中实现Cloneable接口。
&2.protected修饰的clone方法
在java.lang.Object的中,他将clone方法设置为protected修饰,这是很特殊的一种情况。protected的作用域是:包可见+可继承。之所以这样设置,是因为这个方法要返回的是克隆出来的对象,即clone方法要去克隆的类型是未知的,没有办法确定返回值的类型,自然只能让子孙后代来实现它重写它,为了能够让后代继承而又不过与张开,设置为了protected类型。
3.实现clone方法需要实现Cloneable接口
那么我们重写clone方法的时候为什么要去实现Cloneable接口呢?事实上,Cloneable接口是java中的一个标记接口,标记接口是指那些没有方法和属性的接口,他们存在只是为了让大家知道一些信息,而且在用:xxx instanceof Cloneable 的时候可以进行判断。Cloneable这个接口的出现就是为了让设计者知道要进行克隆处理了。如果一个对象需要克隆,但是没有实现(实际上,这里的“实现”换成“写上”更准确)Cloneable接口,那么会产生一个已检验异常。
4.实现clone方法需要调用父类的clone
我们为了达到复制一个和调用方法的这个对象一模一样的对象的目的,我们需要使用父类的clone方法,父类也以此类推,知道达到了Object的clone方法,那么Object的clone方法有什么用呢?API中是这样说的:
protected Object clone( ) throws CloneNotSupportedException
创建并返回此对象的一个副本。
“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,
表达式: x.clone() != x为 true,
表达式: x.clone().getClass() == x.getClass()也为 true,
但这些并非必须要满足的要求。
一般情况下:
x.clone().equals(x)为 true,但这并非必须要满足的要求。
按照惯例,返回的对象应该通过调用 super.clone 获得。
如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。
上面就是API中对clone的一部分基本讲解。我们可以得出结论的是,只要合理的调用了spuer.clone( )它就会返回一个被克隆的对象。在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。在这个克隆对象中,所有的属性都和被克隆的对象的属性相同,而这些相同的属性分为两种:
第一种 : 八大原始类型和不可变的对象(比如String)
第二种 : 其他类对象
对于第一种,clone方法将他们的值设置为原对象的值,没有任何问题。对于第二种,clone方法只是简单的将复制的新对象的引用指向原对象指向的引用,第二种的类对象会被两个对象修改。那么这个时候就涉及一个深浅拷贝的概念了。
(二)浅拷贝
浅拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
比如举个例子,一个类A中有另外一个类B类型的变量。在A重写clone函数调用super.clone的时候,创建的新对象和原来对象中的类B类型的变量是同一个,他们指向了同一个B的类型变量。如果在A中对B的变量做了修改,在新的拷贝出来的对象中B的变量也会被同样的修改。
请记住,直接调用super.clone实现的clone方法全部都是浅拷贝。
(三)深拷贝
深拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
通俗的说,如果说浅拷贝,开始的时候是两条线,如果在最后有一个其他类的变量,那么这两条线最后会合二为一,共同指向这变量,都能对他进行操作。深拷贝则是完完全全的两条线,互不干涉,因为他已经把所有的内部中的变量的对象全都复制一遍了。
深拷贝在代码中,需要在clone方法中多书写调用这个类中其他类的变量的clone函数。
(四)串行化深拷贝
在框架中,有的时候我们发现其中并没有重写clone方法,那么我们在需要拷贝一个对象的时候是如何去操作的呢?答案是我们经常会使用串行化方法,实现Serializable接口。
去寻找其他的方法来替代深拷贝也是无可奈何的事情,如果采用传统的深拷贝,难道你拷贝一个对象的时候向其中追无数层来拷贝完所有的对象变量么?先不谈这么做的时间消耗,仅仅是写这样的代码都会让人望而生畏。串行化深拷贝就是这样一个相对简单的方法。
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。
上面是网上的专业解释,我也不在这里班门弄斧了。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。
public Object deepClone()
//写入对象
ByteArrayOutoutStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo=new ObjectOutputStream(bo);
oo.writeObject(this);
//读取对象
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return(oi.readObject());
虽然这种学院派的代码看起来很复杂,其实只是把对象放到流里,再拿出来。相比较分析判断无数的clone,这样简直是再简单不过了。这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient。
transient:一个对象只要实现了Serilizable接口,这个对象就可以被序列化(序列化是指将java代码以字节序列的形式写出,即我们上面代码前三行写入对象),Java的这种序列化模式为开发者提供了很多便利,可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个的所有属性和方法都会自动序列化。但是有种情况是有些属性是不需要序列号的,所以就用到这个关键字。只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
(五)总结
在实际的应用中,深拷贝和浅拷贝只是两个概念,不一定谁比谁好,要按照实际的工作来确定如何去拷贝一个对象。如果在数据库操作方面,为了取出一张表时不涉及其他的表,肯定需要使用浅拷贝,而在框架的Serializable中,虽然耗时,但是深拷贝是非常有必要的。
&感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具拒绝访问 | www.ggdoc.com | 百度云加速
请打开cookies.
此网站 (www.ggdoc.com) 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(3ddf034a3e984382-ua98).
重新安装浏览器,或使用别的浏览器java深拷贝和浅拷贝的区别_百度知道
java深拷贝和浅拷贝的区别
我有更好的答案
printStackTrace();ByteArrayOutputStream();&&&&of&Object&the&object&out&{&&&&&&&&&&//&Write&&to&&&ObjectOutputStream&=&new&nbsp,对象内部存在的指向其他对象数组或者引用则不复制深拷贝;&oldObj)&{&&nbsp:对象,对象内部的引用均复制示例;&new&ObjectOutputStream(bos);&&&&&&&&&&out.writeObject(oldObj);&&&&&&&&&&out.flush();&&&&&&&&&&out.close();&&&&&&&&&&&&//&Retrieve&an&input&&&copy&&&ObjectInputStream(bis);&&&&&&&&&&obj&&&&&nbsp.&nbsp:public&static&Object&copy(Object&nbsp.printStackTrace();ByteArrayInputStream&&&}&catch&&nbsp.readObject();&&&&&&}&&&&&&&&&&try&&nbsp:只复制一个对象;in&nbsp.toByteArray());&&&&&&&&&&&ObjectInputStream&&and&read&&&&&&&&&&//&a&&&&bis&=&&&&=&obj&=&{&&&&&&&&&&&&&&&from&e;out&=&a&byte&array&ByteArrayOutputStream&bos&&&&&&&=&new&&&&(ClassNotFoundException&cnfe)&&&&stream&&the&object&back&&new&ByteArrayInputStream(&the&byte&array&catch&(IOException&e)&{&&&nbsp浅拷贝
采纳率:59%
为您推荐:
其他类似问题
java的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。Java 中的浅拷贝与深拷贝
#长按上图识别二维码,参与OSC源创会年终盛典#
英文原文:Shallow vs. Deep Copy in Java
链接:https://dzone.com/articles/java-copy-shallow-vs-deep-in-which-you-will-swim
译者:leoxu
本文来自于我提供的免费的 Java 8课程,里面会针对深拷贝和浅拷贝的不同之处进行讨论。你可以在这儿 下载到PPT以及PDF文档。
什么是拷贝?
开始之前,我要先强调一下 Java 中的拷贝是什么。首先,让我们对引用拷贝和对象拷贝进行一下区分。 引用拷贝, 正如它的名称所表述的意思, 就是创建一个指向对象的引用变量的拷贝。如果我们有一个 Car 对象,而且让 myCar 变量指向这个变量,这时候当我们做引用拷贝,那么现在就会有两个 myCar 变量,但是对象仍然只存在一个。
对象拷贝会创建对象本身的一个副本。因此如果我们再一次服务我们 car 对象,就会创建这个对象本身的一个副本, 同时还会有第二个引用变量指向这个被复制出来的对象。
什么是对象?
深拷贝和浅拷贝都是对象拷贝, 但一个对象实际是什么呢? 当我们谈论到对象时,我们经常会说它就像一粒浑圆的咖啡豆,已经是一个不能够被进一步分解的单位了,但这种说法太过于简化了。
比方说我们有一个 Person 对象。这个 Person 对象实际上是由其它的对象组合而成的。如示例 4 所示, Person 对象包含了一个 Name 对象和一个 Address 对象。Name 对象又包含了一个 FirstName 对象和一个 LastName 对象;Address 对象又是由一个 Street 对象以及一个 City 对象组合而成的。那么当我们讨论本文中的这个 Person 时,实际上我是在讨论这些个对象所组成的整个的对象联系网络。
那么为什么我会要对这个 Person 对象进行拷贝呢? 对象复制,经常也会被称作克隆,它是在我们想要修改或者移除某个对象,但仍然想要保留原来的那个对象时所要进行的操作。在另外一篇文章中你可以了解到许多拷贝一个对象的不同方法。在本文中我们将特别讲到如何利用拷贝构造器来创建拷贝。
首先让我们来说说浅拷贝。 对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。"里面的对象“会在原来的对象和它的副本之间共享。例如,我们会为一个 Person对象创建第二个 Person 对象, 而两个 Person 会共享相同的 Name 和 Address 对象。
让我们来看看代码示例。在示例 5 中,我们有一个类 Person,类里面包含了一个 Name 和 Address 对象。拷贝构造器会拿到 originalPerson 对象,然后对其应用变量进行复制。
浅拷贝的问题就是两个对象并非独立的。如果你修改了其中一个 Person 对象的 Name 对象,那么这次修改也会影响奥另外一个 Person 对象。
让我们在示例中看看这个问题。假如说我们有一个 Person 对象,然后也会有一个引用变量 monther 来指向它;然后当我们对 mother 进行拷贝时,创建第二个 Person 对象 son。如果在此后的代码中, son 尝试用 moveOut() 来修改他的 Address 对象, 那么 mother 也会跟着他一起搬走!
这种现象之所以会发生,是因为 mother 和son 对象共享了相同的 Address 对象,如你在示例 7 中所看到的描述。当我们在一个对象中修改了 Address 对象,那么也就表示两个对象总的 Address 都被修改了。
不同于浅拷贝,深拷贝是一个整个独立的对象拷贝。如果我们对整个 Person对象进行深拷贝,我们会对整个对象的结构都进行拷贝。
如你在示例 8 中所见,对一个 Person 的Address对象进行了修改并不会对另外一个对象造成影响。当我们观察示例 9 中的代码,会发现我们不单单对 Person 对象使用了拷贝构造器,同时也会对里面的对象使用拷贝构造器。
使用这种深拷贝,我们可以重新尝试示例 6 中的 mother-son 这个用例。现在 son 可以成功的搬走了!
不过,故事到这儿并没有结束。 要创建一个真正的深拷贝,就需要我们一直这样拷贝下去,一直覆盖到 Person 对象所有的内部元素, 最后只剩下原始的类型以及“不可变对象(Immutables)”。让我们观察下如下这个 Street 类以获得更好的理解:
Street 对象有两个实体变量组成 – String 类型的 name 以及 int 类型的 number。int 类型的 number 是一个原始类型,并非对象。它只是一个简单的值,不能共享, 因此在创建第二个实体变量时,我们可以自动创建一个独立的拷贝。String 是一个不可变对象(Immutable)。简言之,不可变对象也是对象,可一旦创建好了以后就再也不能被修改了。因此,你可以不用为其创建深拷贝就能对其进行共享。
作为总结,我要说说上面在 mother-son 示例中所用到的一些编码技术。只是因为 深拷贝可以让你修改一个对象里面的详细信息,比如 Address 对象,这并不意味着你就该这样做。这样做会提高代码的质量, 因为它可以使得 Person 更容易修改 – 不管 Address 类什么时候被修改了,你也都会要修改应用到 Person 类。例如,如果 Address 类型不再包含 Street 对象了,我们就得根据已经对 Address 类做出的修改来对Person 类中的 moveOut() 方法进行修改。
在本文的示例 6 中,我只选择使用了一个新的 Street 和 City 对象,这样可以更好的对浅拷贝和深拷贝的不同之处进行描述。不过,我会建议你给方法分配一个新的 Address 对象,这样能有效的将其转换成一个浅拷贝和深拷贝的混合体, 见示例 10:
在面向对象领域,这样做违背了封装的原则,因此应该被避免。封装是面向对象编程中一个最重要的方面。在这里,我已经违背封装的原则,对 Person 类中 Address 对象的内部细节进行了访问。这样做对我们的代码造成了伤害,因为我们现在跟 Person 类中的 Address 类纠缠在一起,如果对 Address 类进行了修改,就会如我上面所解释的对代码造成伤害。不过是你显然是会需要将你定义的各种类互相联系在一起以构成代码工程的,但在你要将两个类联系在一起时,需要好好分析一下成本和收益。
点击“阅读原文”查看更多精彩内容
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
今日搜狐热点
她是中的绝色美人 年逾50丁克无子渐析java的浅拷贝和深拷贝 - ImportNew
首先来看看浅拷贝和深拷贝的定义:
浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝。
深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。这个方式称为深拷贝
也就是说浅拷贝只复制一个对象,传递引用,不能复制实例。而深拷贝对对象内部的引用均复制,它是创建一个新的实例,并且复制实例。
对于浅拷贝当对象的成员变量是基本数据类型时,两个对象的成员变量已有存储空间,赋值运算传递值,所以浅拷贝能够复制实例。但是当对象的成员变量是引用数据类型时,就不能实现对象的复制了。
存在一个对象Person,代码如下:
public class Person {
public Person(String name,String sex,int age){
this.name =
this.sex =
this.age =
public Person(Person p){
//拷贝构造方法,复制对象
this.name = p.
this.sex = p.
this.age = p.
上面的对象Person有三个成员变量。name、sex、age。两个构造方法。第二个的参数为该对象,它称为拷贝构造方法,它将创建的新对象初始化为形式参数的实例值,通过它可以实现对象复制功能。
又有一个对象Asian,如下:
public class Asian {
public Asian(String skin,Person person){
this.skin =
this.person =
//引用赋值
public Asian(Asian asian){
//拷贝构造方法,复制对象
this(asian.skin,asian.person);
上面对象也存在着两个成员变量,skin 和Person对象
对于person对象有如下:
Person p1 = new Person(&李四&,&mam&,23);
Person p2 = new Person(P1);
当调用上面的语句时。P2对象将会对P1进行复制。执行情况如下如下图:
对于Asian对象有:
Asian a1 = new Asian(&yellow&,new Person(&李四&,&mam&,23));
Asian a2 = new Asian(a1);
New Asian(a1)执行Asian类的拷贝构造方法,由于对象赋值是引用赋值。使得a1和a2引用同一个对象
当a1执行某条可以改变该值的语句时,那么a1将会通过这个语句也可以改变a2对象的成员变量
如果执行以下语句:a2.name = new Person(a1.name)
这时将会创建一个新的Person对象
不同包的类是无法访问default修饰的类的,所以无法继承,不会有子类和父类的情况。所以第二种情况是...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:ImportNew.
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2018 ImportNew}

我要回帖

更多关于 java的浅拷贝和深拷贝 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信