1、概述

在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。例如下面程序展示的情况:

class Student {  private int number;  public int getNumber() {  return number;  }  public void setNumber(int number) {  this.number = number;  }  }
public class Test {   public static void main(String args[]) {  Student stu1 = new Student();  stu1.setNumber(12345);  Student stu2 = stu1;  stu1.setNumber(54321);  System.out.println("学生1:" + stu1.getNumber());  System.out.println("学生2:" + stu2.getNumber());  }
}

结果:

学生1:54321
学生2:54321

为什么改变学生2的学号,学生1的学号也发生了变化呢?
原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,
这样,stu1和stu2指向内存堆中同一个对象。如图:

那么,怎么能干干净净清清楚楚地复制一个对象呢。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求有很多途径,
(1)将A对象的值分别通过set方法加入B对象中;
(2)通过重写java.lang.Object类中的方法clone();
(3)通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制;
(4)通过序列化实现对象的复制。

2、将A对象的值分别通过set方法加入B对象中

对属性逐个赋值,本实例为了演示简单就设置了一个属性:

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = new Student();
stu2.setNumber(stu1.getNumber());

我们发现,属性少对属性逐个赋值还挺方便,但是属性多时,就需要一直get、set了,非常麻烦。

3、重写java.lang.Object类中的方法clone()

先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。

在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

3.1 浅克隆

一般步骤:

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

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

class Student implements Cloneable{  private int number;  public int getNumber() {  return number;  }  public void setNumber(int number) {  this.number = number;  }  @Override  public Object clone() {  Student stu = null;  try{  stu = (Student)super.clone();  }catch(CloneNotSupportedException e) {  e.printStackTrace();  }  return stu;  }
}
public class Test {  public static void main(String args[]) {  Student stu1 = new Student();  stu1.setNumber(12345);  Student stu2 = (Student)stu1.clone();  System.out.println("学生1:" + stu1.getNumber());  System.out.println("学生2:" + stu2.getNumber());  stu2.setNumber(54321);  System.out.println("学生1:" + stu1.getNumber());  System.out.println("学生2:" + stu2.getNumber());  }
}

结果:

学生1:12345
学生2:12345
学生1:12345
学生2:54321

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。

3.2 深克隆

package abc;  class Address implements Cloneable {  private String add;  public String getAdd() {  return add;  }  public void setAdd(String add) {  this.add = add;  }  @Override  public Object clone() {  Address addr = null;  try{  addr = (Address)super.clone();  }catch(CloneNotSupportedException e) {  e.printStackTrace();  }  return addr;  }
}  class Student implements Cloneable{  private int number;  private Address addr;  public Address getAddr() {  return addr;  }  public void setAddr(Address addr) {  this.addr = addr;  }  public int getNumber() {  return number;  }  public void setNumber(int number) {  this.number = number;  }  @Override  public Object clone() {  Student stu = null;  try{  stu = (Student)super.clone();   //浅复制  }catch(CloneNotSupportedException e) {  e.printStackTrace();  }  stu.addr = (Address)addr.clone();   //深度复制  return stu;  }
}
public class Test {  public static void main(String args[]) {  Address addr = new Address();  addr.setAdd("杭州市");  Student stu1 = new Student();  stu1.setNumber(123);  stu1.setAddr(addr);  Student stu2 = (Student)stu1.clone();  System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  addr.setAdd("西湖区");  System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  }
}

结果:

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

怎么两个学生的地址都改变了?

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

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

package abc;  class Address implements Cloneable {  private String add;  public String getAdd() {  return add;  }  public void setAdd(String add) {  this.add = add;  }  @Override  public Object clone() {  Address addr = null;  try{  addr = (Address)super.clone();  }catch(CloneNotSupportedException e) {  e.printStackTrace();  }  return addr;  }
}  class Student implements Cloneable{  private int number;  private Address addr;  public Address getAddr() {  return addr;  }  public void setAddr(Address addr) {  this.addr = addr;  }  public int getNumber() {  return number;  }  public void setNumber(int number) {  this.number = number;  }  @Override  public Object clone() {  Student stu = null;  try{  stu = (Student)super.clone();   //浅复制  }catch(CloneNotSupportedException e) {  e.printStackTrace();  }  stu.addr = (Address)addr.clone();   //深度复制  return stu;  }
}
public class Test {  public static void main(String args[]) {  Address addr = new Address();  addr.setAdd("杭州市");  Student stu1 = new Student();  stu1.setNumber(123);  stu1.setAddr(addr);  Student stu2 = (Student)stu1.clone();  System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  addr.setAdd("西湖区");  System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  }
}

结果:

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

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

(如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)

4、工具类BeanUtils和PropertyUtils进行对象复制

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = new Student();
BeanUtils.copyProperties(stu2,stu1);

这种写法无论多少种属性都只需要一行代码搞定,很方便吧!除BeanUtils外还有一个名为PropertyUtils的工具类,它也提供copyProperties()方法,作用与BeanUtils的同名方法十分相似,主要的区别在于BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。

5、通过序列化实现对象的复制

序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

java复制对象_JAVA对象复制的方法相关推荐

  1. java将a对象转换为b对象_Java 对象的深复制五种方式

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

  2. java 数组效率_java数组复制的四种方法效率对比

    有关数组的基础知识,有很多方面,比方说初始化,引用,遍历,以及一维数组和二维数组,今天我们先看看数组复制的有关内容. 来源于牛客网的一道选择题: JAVA语言的下面几种数组复制方法中,哪个效率最高? ...

  3. java 复制文件_Java中复制文件的4种方法

    Java拷贝文件是一种非常常见的操作.但是java.io.File类没有任何快捷方法可以将文件从源复制到目标文件.在这里,我们将了解学习可以在java中复制文件的四种不同方法. 方法一:使用Strea ...

  4. java finalize逃脱_Java对象回收与finalize方法

    生存还是死亡 事实上,即使被可达性分析算法判断为不可达,对象也不是"非死不可",对象会先进入"缓刑"状态.要真正宣告一个对象死亡,至少要经历两次标记过程: 如果 ...

  5. java浅度克隆_java对象 深度克隆(不实现Cloneable接口)和浅度克隆

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

  6. java 克隆一个对象_Java对象克隆

    Java对象克隆 1 什么是Java对象克隆 对象克隆是创建一个对象的副本的方式.Object类的clone() 方法用于克隆对象. java.lang.Cloneable接口必须由我们要建立其对象克 ...

  7. java初始化实例化_Java对象的创建过程:类的初始化与实例化

    一.Java对象创建时机 我们知道,一个对象在可以被使用之前必须要被正确地实例化.在Java代码中,有很多行为可以引起对象的创建,最为直观的一种就是使用new关键字来调用一个类的构造函数显式地创建对象 ...

  8. java字符串序列化_java对象序列化为字符串

    1 场景 java对象某些时候,需要序列化成字符串存储在数据库中,需要的时候,再将字符串反序列化为java对象. 如使用shiro缓存分布式session,需要将session对象序列化成字符串存储在 ...

  9. java arraycopy 参数_java 数组复制:System.arrayCopy 深入解析

    先看ArrayList源码中数组复制的代码: 其实ArrayList 就是一个数组的形式存放数据的.没有高深的地方. 他的性能在于他的索引能力,正因为他是数组形式,所以索引元素的时候他表现得非常的快速 ...

最新文章

  1. 十九种损失函数,你能认识几个?
  2. 【动态规划】矩阵嵌套问题
  3. WPF-3D动效-文字球形环绕
  4. 想理解Java的IO,不要从操作系统开始说起的都是耍流氓...
  5. win7蓝牙怎么连接_台式机蓝牙怎么连接
  6. vue项目中阻止浏览器返回上一页
  7. 使用Java和Spring构建现代Web应用程序
  8. Mybatis四种分页方式
  9. c# 微服务学习_资深架构师学习笔记:什么是微服务?
  10. 云计算-从基础到应用架“.NET研究”构系列-云计算的演进
  11. iphone怎么检测屏幕是否被点亮 (用UIApplication的Delegate)
  12. 刷屏!黄晓明杨颖官宣离婚,两人已无商业关联
  13. wide and deep及其发展
  14. java毕业设计成品基于SSM+Bootstrap+MYSQL演唱会网上订票系统
  15. python采用面向对象编程模式吗_python面向对象编程练习
  16. linux中syscmd用法,M4 宏处理器
  17. Dreamweaver网页作业——紫罗兰永恒花园动漫价绍网页 7页,含有table表格,js表单验证还有首页视频。以及列表页。浮动布局。div+css+js
  18. linux如何查看证书过期时间,shell脚本:通过域名获取证书的过期时间
  19. 浅谈因子分析(Factor Analysis)
  20. 交换机芯片初创企业再获7700万美元融资

热门文章

  1. 华为鸿蒙os正在国外小规模测试,华为鸿蒙OS正小规模测试
  2. 验证码一(验证码生成)
  3. SkinSharp用法
  4. angular 实现无限极联动下拉
  5. linux网络编程之socket编程(六)
  6. SQL导入Excel数据时,数字中混有字符将导致数据丢失的解决办法
  7. 博图买什么样配置的笔记本_3dsmax需要什么样的笔记本配置?
  8. ehcache缓存原理_干掉GuavaCache:Caffeine才是本地缓存的王
  9. 从零开始学前端:CSS盒子模型属性 --- 今天你学习了吗?(CSS:Day14)
  10. 董老师又双叒叕送书啦,8本《Python网络程序设计(微课版)》