熟悉C++的朋友对这个话题应该很熟悉,浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象。深拷贝就是两个对象的值相等,但是互相独立。本来想把以前写的一篇文章扩充一下,没想到居然牵扯出很多复杂的问题。本文测试环境是windows xp sp3中文版、NetBeans6.7.1,JDK1.6-update16。这里抛砖引玉,希望大家能提宝贵意见。

    首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:

  1. 实现 Cloneable接口,因为 Object的 clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。

  2. 改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作, Object的 clone方法是 protected权限。


    通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
    现在,来看一下不同的类型在拷贝过程中的表现:


Operator =
拷贝构造函数
clone方法
预定义非集合类型
深拷贝
如果支持拷贝构造函数的类型,则是深拷贝
不支持
自定义类型
浅拷贝
取决于实现
取决于实现
预定义集合类型
浅拷贝
会逐个调用每个元素的operator=方法
会逐个调用每个元素的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的深拷贝和浅拷贝相关推荐

  1. 学习Java的深拷贝和浅拷贝

    关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用 ...

  2. Java基础-深拷贝和浅拷贝的区别

    深拷贝与浅拷贝 一般来说,拷贝的类型分为 深拷贝与浅拷贝. |-----------------------------| | 深拷贝:引用对象的值等信息,复制一份一样的.             | ...

  3. java数组深拷贝和浅拷贝_java List复制:浅拷贝与深拷贝

    Java的拷贝可以分为三种:浅拷贝(Shallow Copy).深拷贝(Deep Copy).延迟拷贝(Lazy Copy). 在java中除了基本数据类型之外(int,long,short等),还存 ...

  4. java:深拷贝与浅拷贝

    拷贝的实现: 只有子类实现了Cloneable接口后才可以使用Object类提供的clone方法. protected native Object clone() throws CloneNotSup ...

  5. java:clone 深拷贝与浅拷贝,为什么要慎用浅拷贝

    转自:https://blog.csdn.net/qq_34110755/article/details/79914639 1.浅拷贝 对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此 ...

  6. 【Java】深拷贝和浅拷贝,Cloneable接口

    活动地址:CSDN21天学习挑战赛 ✨博客主页: XIN-XIANG荣 ✨系列专栏:[Java SE] ✨一句短话: 难在坚持,贵在坚持,成在坚持! 文章目录 1. Cloneable接口的介绍 2. ...

  7. 深入理解java 的深拷贝和浅拷贝

    一 深拷贝和钱拷贝的概念 1.深拷贝 将所有的引用对象拷贝.例如a引用b,b引用c,在拷贝得到a'时,a'里面的引用对象是b',b'里面的引用对象是c'. 直接输入a和a',看到他们是不同的引用地址. ...

  8. java数组深拷贝和浅拷贝_java中的深拷贝与浅拷贝(值类型 vs 引用类型)

    对象赋值 赋值是日常编程过程中最常见的操作,最简单的比如: Student codeSheep = new Student(); Student codePig = codeSheep; 严格来说,这 ...

  9. JAVA中深拷贝与浅拷贝(在网上找到的) 希望对于理解深拷贝与浅拷贝有帮助...

     什么是clone? 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就 ...

最新文章

  1. doker zookeeper kafka单机搭建
  2. 蜕变与成长中的青春创作:评论家谈少数民族青年作家的创作
  3. linux inputuevent使用
  4. 使用Tab Bar切换视图
  5. 演练 鼠划图片上变亮的效果 1022
  6. android java 指针异常处理,Android自定义抛出异常的方法详解
  7. C语言union类型和C语言 uchar类型的个人见解
  8. 没有com.sun.tools.javac.main的解决办法
  9. 形式语言与自动机之一 语言与文法
  10. JAVA最强工具类之一HuTool
  11. php 去除单引号,php如何去除双引号
  12. 某网站cookie加密黑盒调用与算法还原
  13. 0.《JavaScript高级程序设计》(Nicholas C.Zakas 第3版)
  14. 什么是url,herf和src的区别
  15. Leetcode 1125:最小的必要团队
  16. N条线段求交的扫描线算法
  17. 微信小程序--放入个性化手绘地图具体步骤(腾讯地图)
  18. 为什么要有不同的参考文献格式?
  19. oracle账户别名,Oracle的别名
  20. 简单的一些网络流问题

热门文章

  1. MySQL数据库分表分区
  2. Servlet WEB过滤器
  3. ORM学员管理系统单表查询示例
  4. 2017软件工程实践第二次作业(数独)
  5. LINUX 下 WEBlogic集群的搭建-01
  6. php5,Apache在windows 7环境搭建
  7. 如何在 Linux 上用 SQL 语句来查询 Apache 日志
  8. OpenJudge/Poj 2027 No Brainer
  9. 随机数生成算法-二谈
  10. 《当程序员的那些狗日日子》(五)工作中,工作外