摘自:http://blog.csdn.net/tounaobun/article/details/8491392

假如说你想复制一个简单变量。很简单:

  1. int apples = 5;
  2. int pears = apples;

不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。

但是如果你复制的是一个对象,情况就有些复杂了。

假设说我是一个beginner,我会这样写:

  1. class Student {
  2. private int number;
  3. public int getNumber() {
  4. return number;
  5. }
  6. public void setNumber(int number) {
  7. this.number = number;
  8. }
  9. }
  10. public class Test {
  11. public static void main(String args[]) {
  12. Student stu1 = new Student();
  13. stu1.setNumber(12345);
  14. Student stu2 = stu1;
  15. System.out.println("学生1:" + stu1.getNumber());
  16. System.out.println("学生2:" + stu2.getNumber());
  17. }
  18. }

打印结果:

  1. 学生1:12345
  2. 学生2:12345

这里我们自定义了一个学生类,该类只有一个number字段。

我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)

再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,

难道真的是这样吗?

我们试着改变stu2实例的number字段,再打印结果看看:

  1. stu2.setNumber(54321);
  2. System.out.println("学生1:" + stu1.getNumber());
  3. System.out.println("学生2:" + stu2.getNumber());

打印结果:

  1. 学生1:54321
  2. 学生2:54321

这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?

原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,

这样,stu1和stu2指向内存堆中同一个对象。如图:

那么,怎样才能达到复制一个对象呢?

是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。

该方法的签名是:

protected native Object clone() throws CloneNotSupportedException;

因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。

要想对一个对象进行复制,就需要对clone方法覆盖。

一般步骤是(浅复制):

1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)

2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)

下面对上面那个方法进行改造:

  1. class Student implements Cloneable{
  2. private int number;
  3. public int getNumber() {
  4. return number;
  5. }
  6. public void setNumber(int number) {
  7. this.number = number;
  8. }
  9. @Override
  10. public Object clone() {
  11. Student stu = null;
  12. try{
  13. stu = (Student)super.clone();
  14. }catch(CloneNotSupportedException e) {
  15. e.printStackTrace();
  16. }
  17. return stu;
  18. }
  19. }
  20. public class Test {
  21. public static void main(String args[]) {
  22. Student stu1 = new Student();
  23. stu1.setNumber(12345);
  24. Student stu2 = (Student)stu1.clone();
  25. System.out.println("学生1:" + stu1.getNumber());
  26. System.out.println("学生2:" + stu2.getNumber());
  27. stu2.setNumber(54321);
  28. System.out.println("学生1:" + stu1.getNumber());
  29. System.out.println("学生2:" + stu2.getNumber());
  30. }
  31. }

打印结果:

  1. 学生1:12345
  2. 学生2:12345
  3. 学生1:12345
  4. 学生2:54321

如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:

  1. System.out.println(stu1 == stu2); // false

上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):

我们在学生类里再加一个Address类。

  1. class Address  {
  2. private String add;
  3. public String getAdd() {
  4. return add;
  5. }
  6. public void setAdd(String add) {
  7. this.add = add;
  8. }
  9. }
  10. class Student implements Cloneable{
  11. private int number;
  12. private Address addr;
  13. public Address getAddr() {
  14. return addr;
  15. }
  16. public void setAddr(Address addr) {
  17. this.addr = addr;
  18. }
  19. public int getNumber() {
  20. return number;
  21. }
  22. public void setNumber(int number) {
  23. this.number = number;
  24. }
  25. @Override
  26. public Object clone() {
  27. Student stu = null;
  28. try{
  29. stu = (Student)super.clone();
  30. }catch(CloneNotSupportedException e) {
  31. e.printStackTrace();
  32. }
  33. return stu;
  34. }
  35. }
  36. public class Test {
  37. public static void main(String args[]) {
  38. Address addr = new Address();
  39. addr.setAdd("杭州市");
  40. Student stu1 = new Student();
  41. stu1.setNumber(123);
  42. stu1.setAddr(addr);
  43. Student stu2 = (Student)stu1.clone();
  44. System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  45. System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  46. }
  47. }

打印结果:

  1. 学生1:123,地址:杭州市
  2. 学生2:123,地址:杭州市

乍一看没什么问题,真的是这样吗?

我们在main方法中试着改变addr实例的地址。

  1. addr.setAdd("西湖区");
  2. System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  3. System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());

打印结果:

  1. 学生1:123,地址:杭州市
  2. 学生2:123,地址:杭州市
  3. 学生1:123,地址:西湖区
  4. 学生2:123,地址:西湖区

这就奇怪了,怎么两个学生的地址都改变了?

原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:

  1. package abc;
  2. class Address implements Cloneable {
  3. private String add;
  4. public String getAdd() {
  5. return add;
  6. }
  7. public void setAdd(String add) {
  8. this.add = add;
  9. }
  10. @Override
  11. public Object clone() {
  12. Address addr = null;
  13. try{
  14. addr = (Address)super.clone();
  15. }catch(CloneNotSupportedException e) {
  16. e.printStackTrace();
  17. }
  18. return addr;
  19. }
  20. }
  21. class Student implements Cloneable{
  22. private int number;
  23. private Address addr;
  24. public Address getAddr() {
  25. return addr;
  26. }
  27. public void setAddr(Address addr) {
  28. this.addr = addr;
  29. }
  30. public int getNumber() {
  31. return number;
  32. }
  33. public void setNumber(int number) {
  34. this.number = number;
  35. }
  36. @Override
  37. public Object clone() {
  38. Student stu = null;
  39. try{
  40. stu = (Student)super.clone();   //浅复制
  41. }catch(CloneNotSupportedException e) {
  42. e.printStackTrace();
  43. }
  44. stu.addr = (Address)addr.clone();   //深度复制
  45. return stu;
  46. }
  47. }
  48. public class Test {
  49. public static void main(String args[]) {
  50. Address addr = new Address();
  51. addr.setAdd("杭州市");
  52. Student stu1 = new Student();
  53. stu1.setNumber(123);
  54. stu1.setAddr(addr);
  55. Student stu2 = (Student)stu1.clone();
  56. System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  57. System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  58. addr.setAdd("西湖区");
  59. System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  60. System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  61. }
  62. }

打印结果:

  1. 学生1:123,地址:杭州市
  2. 学生2:123,地址:杭州市
  3. 学生1:123,地址:西湖区
  4. 学生2:123,地址:杭州市

这样结果就符合我们的想法了。

最后我们可以看看API里其中一个实现了clone方法的类:

java.util.Date:

    1. /**
    2. * Return a copy of this object.
    3. */
    4. public Object clone() {
    5. Date d = null;
    6. try {
    7. d = (Date)super.clone();
    8. if (cdate != null) {
    9. d.cdate = (BaseCalendar.Date) cdate.clone();
    10. }
    11. } catch (CloneNotSupportedException e) {} // Won't happen
    12. return d;
    13. }

java中的拷贝(一)的更多相关文章

  1. 总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...

  2. java中的拷贝(二)深克隆

    浅拷贝(Object类中的clone()方法)是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝. 深拷贝(或叫深克隆) 则是对对象及该对象关联的对象内容, ...

  3. Java中对象拷贝的两种方式

    引用的拷贝 //引用拷贝 private static void copyReferenceObject(){ Person p = new Person(23, "zhang") ...

  4. java中的拷贝文件FileChannel

    以前用Java拷贝文件,只知道写byte数组循环拷贝,今天知道了可以用FileChannel进行拷贝,上代码: 下边是传统的byte数组拷贝方法 </pre><pre name=&q ...

  5. Java中的Html解析:使用jsoup

    包:jsoup-1.10.2.jar import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import o ...

  6. 一文了解 Java 中的构造器

    摘要:Java 也采用了构造器,并且还提供了一个垃圾收集器(garbage collector),当不再使用内存资源的时候,垃圾收集器会自动将其释放. 本文分享自华为云社区<一文带你了解 Jav ...

  7. Java学习-045-目录中文件拷贝

    挺晚的了,直接上码.敬请各位小主参阅,若有不足之处,敬请指正,非常感谢! 目录文件拷贝源码: /** * <strong>目录拷贝</strong><br> * & ...

  8. JAVA中JavaBean对象之间拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...

  9. 关于Java中的HashMap的深浅拷贝的测试与几点思考

    0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...

随机推荐

  1. Selenium2学习-010-WebUI自动化实战实例-008-Selenium 操作下拉列表实例-Select

    此文主要讲述用 Java 编写 Selenium 自动化测试脚本编写过程中,对下拉列表框 Select 的操作. 下拉列表是 Web UI 自动化测试过程中使用率非常高的,通常有两种形式的下拉列表,一 ...

  2. Windows-007-进程相关命令(netstat、tasklist、taskkill、tskill)实战实例图文详解

    本节主要讲述 Windows 系统下,nestat.tasklist.tskill 三个 CMD 命令的参数,及使用方法:以及如何利用三者结合查看进程信息和结束进程.敬请亲们参阅,希望能对亲们有所帮助 ...

  3. c#中匿名函数lamb表达式

    c#中匿名函数lamb表达式 实例一:(其实,这样都是些语法糖) using System; using System.Collections.Generic; using System.Linq; ...

  4. 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》

    优化Linux内核参数/etc/sysctl.conf  sysctl  <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...

  5. APICloud上openFrameGroup把菜单挡住了,怎么处理?

    问:openFrameGroup把菜单挡住了,怎么处理? 试了sendFrameToBack没反应,又不能页面自己openFrameGroup,不知道该怎么办.而且用另外的页面先openFrameGr ...

  6. Ubuntu中Apache修改DocumentRoot(修改网站根目录)

    今天配置好Apache+PHP+MySQL但是apache默认DocumentRoot是/var/www想把它改到我Windows下进行测试的k:/wwwroot把 apache2.conf 翻了好几 ...

  7. linux的5个查找命令_转

    转自:http://www.ruanyifeng.com/blog/2009/10/5_ways_to_search_for_files_using_the_terminal.html 在Linux中 ...

  8. mysql centeros 安装

    http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/07/3003278.html linux mysql允许远程连接 1.登录数据库:my ...

  9. Hadoop学习笔记: HDFS

    注:该文内容部分来源于ChinaHadoop.cn上的hadoop视频教程. 一. HDFS概述 HDFS即Hadoop Distributed File System, 源于Google发表于200 ...

  10. 使用Adreno Profiler分析android游戏

    有时候我们需要对自己的游戏或者别人的游戏进行分析,比如我们需要了解一个引擎的大体渲染的流程,这个时候我们可以借助一些工具,在PC上我们可以使用Microsoft PIX.Intel GPA.Nvidi ...