我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常明显。然而我们知道拷贝分为深拷贝和浅拷贝之分,但是浅拷贝存在对象属性拷贝不彻底问题。关于深拷贝、浅拷贝的请参考这里:渐析java的浅拷贝和深拷贝

一、浅拷贝问题

我们先看如下代码:

public class Person implementsCloneable{/**姓名 **/

privateString name;/**电子邮件 **/

privateEmail email;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicEmail getEmail() {returnemail;

}public voidsetEmail(Email email) {this.email =email;

}publicPerson(String name,Email email){this.name =name;this.email =email;

}publicPerson(String name){this.name =name;

}protectedPerson clone() {

Person person= null;try{

person= (Person) super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}returnperson;

}

}public classClient {public static voidmain(String[] args) {//写封邮件

Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议...");

Person person1= new Person("张三",email);

Person person2=person1.clone();

person2.setName("李四");

Person person3=person1.clone();

person3.setName("王五");

System.out.println(person1.getName()+ "的邮件内容是:" +person1.getEmail().getContent());

System.out.println(person2.getName()+ "的邮件内容是:" +person2.getEmail().getContent());

System.out.println(person3.getName()+ "的邮件内容是:" +person3.getEmail().getContent());

}

}--------------------Output:

张三的邮件内容是:请与今天12:30到二会议室参加会议...

李四的邮件内容是:请与今天12:30到二会议室参加会议...

王五的邮件内容是:请与今天12:30到二会议室参加会议...

在该应用程序中,首先定义一封邮件,然后将该邮件发给张三、李四、王五三个人,由于他们是使用相同的邮件,并且仅有名字不同,所以使用张三该对象类拷贝李四、王五对象然后更改下名字即可。程序一直到这里都没有错,但是如果我们需要张三提前30分钟到,即把邮件的内容修改下:

public classClient {public static voidmain(String[] args) {//写封邮件

Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议...");

Person person1= new Person("张三",email);

Person person2=person1.clone();

person2.setName("李四");

Person person3=person1.clone();

person3.setName("王五");

person1.getEmail().setContent("请与今天12:00到二会议室参加会议...");

System.out.println(person1.getName()+ "的邮件内容是:" +person1.getEmail().getContent());

System.out.println(person2.getName()+ "的邮件内容是:" +person2.getEmail().getContent());

System.out.println(person3.getName()+ "的邮件内容是:" +person3.getEmail().getContent());

}

}

在这里同样是使用张三该对象实现对李四、王五拷贝,最后将张三的邮件内容改变为:请与今天12:00到二会议室参加会议...。但是结果是:

张三的邮件内容是:请与今天12:00到二会议室参加会议...

李四的邮件内容是:请与今天12:00到二会议室参加会议...

王五的邮件内容是:请与今天12:00到二会议室参加会议...

这里我们就疑惑了为什么李四和王五的邮件内容也发送了改变呢?让他们提前30分钟到人家会有意见的!

其实出现问题的关键就在于clone()方法上,我们知道该clone()方法是使用Object类的clone()方法,但是该方法存在一个缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择性的拷贝,基本规则如下:

1、 基本类型

如果变量是基本很类型,则拷贝其值,比如int、float等。

2、 对象

如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量。

3、 String字符串

若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有紫都城对象保持不变。

基于上面上面的规则,我们很容易发现问题的所在,他们三者公用一个对象,张三修改了该邮件内容,则李四和王五也会修改,所以才会出现上面的情况。对于这种情况我们还是可以解决的,只需要在clone()方法里面新建一个对象,然后张三引用该对象即可:

protectedPerson clone() {

Person person= null;try{

person= (Person) super.clone();

person.setEmail(newEmail(person.getEmail().getObject(),person.getEmail().getContent()));

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}returnperson;

}

所以:浅拷贝只是Java提供的一种简单的拷贝机制,不便于直接使用。

对于上面的解决方案还是存在一个问题,若我们系统中存在大量的对象是通过拷贝生成的,如果我们每一个类都写一个clone()方法,并将还需要进行深拷贝,新建大量的对象,这个工程是非常大的,这里我们可以利用序列化来实现对象的拷贝。

二、利用序列化实现对象的拷贝

如何利用序列化来完成对象的拷贝呢?在内存中通过字节流的拷贝是比较容易实现的。把母对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与母对象之间并不存在引用共享的问题,真正实现对象的深拷贝。

public classCloneUtils {

@SuppressWarnings("unchecked")public static T clone(T obj){

T cloneObj= null;try{//写入字节流

ByteArrayOutputStream out = newByteArrayOutputStream();

ObjectOutputStream obs= newObjectOutputStream(out);

obs.writeObject(obj);

obs.close();//分配内存,写入原始对象,生成新对象

ByteArrayInputStream ios = newByteArrayInputStream(out.toByteArray());

ObjectInputStream ois= newObjectInputStream(ios);//返回生成的新对象

cloneObj =(T) ois.readObject();

ois.close();

}catch(Exception e) {

e.printStackTrace();

}returncloneObj;

}

}

使用该工具类的对象必须要实现Serializable接口,否则是没有办法实现克隆的。

public class Person implementsSerializable{private static final long serialVersionUID = 2631590509760908280L;

..................//去除clone()方法

}public class Email implementsSerializable{private static final long serialVersionUID = 1267293988171991494L;

....................

}

所以使用该工具类的对象只要实现Serializable接口就可实现对象的克隆,无须继承Cloneable接口实现clone()方法。

public classClient {public static voidmain(String[] args) {//写封邮件

Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议...");

Person person1= new Person("张三",email);

Person person2=CloneUtils.clone(person1);

person2.setName("李四");

Person person3=CloneUtils.clone(person1);

person3.setName("王五");

person1.getEmail().setContent("请与今天12:00到二会议室参加会议...");

System.out.println(person1.getName()+ "的邮件内容是:" +person1.getEmail().getContent());

System.out.println(person2.getName()+ "的邮件内容是:" +person2.getEmail().getContent());

System.out.println(person3.getName()+ "的邮件内容是:" +person3.getEmail().getContent());

}

}-------------------Output:

张三的邮件内容是:请与今天12:00到二会议室参加会议...

李四的邮件内容是:请与今天12:30到二会议室参加会议...

王五的邮件内容是:请与今天12:30到二会议室参加会议...

巩固基础,提高技术,不惧困难,攀登高峰!!!!!!

参考文献《编写高质量代码 改善Java程序的151个建议》----秦小波

java 编码实现内存拷贝_java提高篇(六)-----使用序列化实现对象的拷贝相关推荐

  1. [改善Java代码] 推荐使用序列化实现对象的拷贝

    建议44: 推荐使用序列化实现对象的拷贝 上一个建议说了对象的浅拷贝问题,实现Cloneable接口就具备了拷贝能力,那我们来思考这样一个问题:如果一个项目中有大量的对象是通过拷贝生成的,那我们该如何 ...

  2. 使用序列化实现对象的拷贝(转载)

    文章出处:http://www.cnblogs.com/chenssy/p/3382979.html 实现Cloneable接口:实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面 ...

  3. java 集成_java提高篇(二)-----理解java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  4. Java进阶 JVM 内存与垃圾回收篇(一)

    JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...

  5. java中类型转换的造型_Java总结篇系列:类型转换/造型

    Java中,经常可以遇到类型转换的场景,从变量的定义到复制.数值变量的计算到方法的参数传递.基类与派生类间的造型等,随处可见类型转换的身影.Java中的类型转换在Java编码中具有重要的作用. 首先, ...

  6. java 最大堆内存设置_Java 堆内存

    堆内存 Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象. 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young ).老年代 ( Old ).新生代 ...

  7. java 提高篇_java提高篇(十六)-----异常(一)

    Java的基本理念是"结构不佳的代码不能运行"!!!!! 大成若缺,其用不弊. 大盈若冲,其用不穷. 在这个世界不可能存在完美的东西,不管完美的思维有多么缜密,细心,我们都不可能考 ...

  8. java long常量池_Java提高篇之常量池

    一.相关概念 1. 什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. 2. Class文件中的 ...

  9. java多重继承用在什么地方_Java提高篇——Java实现多重继承

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...

最新文章

  1. 详解:UML类图符号、各种关系说明以及举例
  2. UML博客建模--用例图
  3. VC中实现GCC的2个比较常用的位运算函数
  4. 【渝粤教育】电大中专会计电算化_1作业 题库
  5. 人不是被事物本身困扰是什么理论_你常常被负面情绪困扰么?怎么释放和解决负面情绪...
  6. 永州计算机职称考试网,永州2016年11月职称计算机考试时间
  7. 20190324每日一句:生活中的困难使我更加强大​​​​​​​
  8. cocos creator 数组_5Cocos Creator 脚本简介
  9. win32汇编 钩子的编写与使用
  10. ISO9001:2000标准的主要特点和要求(转载)
  11. 排版侠html怎么复制,排版侠| 3分钟完美编辑变排版达人
  12. view基础知识介绍(一)
  13. 宽屏扁平化结婚恋爱整站HTML5模板
  14. CRM下午茶(九)-老客户挽回
  15. dat文件导入cad画图步骤_图说CAD|多文件、多布局图纸批量打印设置的8个关键步骤...
  16. WordPress站点快速集成腾讯数字身份管控平台CIAM,免开发实现登录认证
  17. FlashDB数据库+Norflash移植和使用
  18. [Matlab]入门教程基础向笔记(B站视频)
  19. 水文气象数据——全球地面观测降水
  20. bootstrap4.0图标使用_Bootstrap4 glyphicon 移除图标 glyphicon fonts-faces 解决方案

热门文章

  1. oracle 内存分析工具,IDE 中的分析工具
  2. text 两端对齐 小程序_小程序实现文字两端对齐
  3. C++中set和map的erase用法
  4. 带虚函数的类的sizeof分析
  5. pytorch实现数据增强的原理
  6. Halcon学习笔记:1D Measuring一维测量_fuse.hdev灯丝测量示例
  7. ROS 学习笔记(二):自定义消息msg+Publisher+Subscriber 示例运行
  8. 数字签名时间戳服务器的原理
  9. Inside Class Loaders
  10. java+long是什么_Java中long的模运算符是什么? - java