0

0

java提高篇(五)-----使用序列化实现对象的拷贝

黄舟

黄舟

发布时间:2017-02-09 13:39:07

|

1301人浏览过

|

来源于php中文网

原创

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

       一、浅拷贝问题

      我们先看如下代码:

public class Person implements Cloneable{  
    /** 姓名 **/  
    private String name;  
      
    /** 电子邮件 **/  
    private Email email;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public Email getEmail() {  
        return email;  
    }  
  
    public void setEmail(Email email) {  
        this.email = email;  
    }  
      
    public Person(String name,Email email){  
        this.name  = name;  
        this.email = email;  
    }  
      
    public Person(String name){  
        this.name = name;  
    }  
  
    protected Person clone() {  
        Person person = null;  
        try {  
            person = (Person) super.clone();  
        } catch (CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
          
        return person;  
    }  
}  
  
public class Client {  
    public static void main(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 class Client {  
    public static void main(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到二会议室参加会议...

立即学习Java免费学习笔记(深入)”;

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

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

      1、 基本类型

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

      2、 对象

Aha
Aha

全天候网红营销AI智能体平台

下载

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

      3、 String字符串

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

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

protected Person clone() {  
        Person person = null;  
        try {  
            person = (Person) super.clone();  
            person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));  
        } catch (CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
          
        return person;  
    }

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

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

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

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

public class CloneUtils {  
    @SuppressWarnings("unchecked")  
    public static <T extends Serializable> T clone(T obj){  
        T cloneObj = null;  
        try {  
            //写入字节流  
            ByteArrayOutputStream out = new ByteArrayOutputStream();  
            ObjectOutputStream obs = new ObjectOutputStream(out);  
            obs.writeObject(obj);  
            obs.close();  
              
            //分配内存,写入原始对象,生成新对象  
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(ios);  
            //返回生成的新对象  
            cloneObj = (T) ois.readObject();  
            ois.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return cloneObj;  
    }  
}

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

public class Person implements Serializable{  
    private static final long serialVersionUID = 2631590509760908280L;  
  
    ..................  
    //去除clone()方法  
  
}  
  
public class Email implements Serializable{  
    private static final long serialVersionUID = 1267293988171991494L;  
      
    ....................  
}

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

public class Client {  
    public static void main(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提高篇(五)-----使用序列化实现对象的拷贝的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 生态工具与框架:扩展开发能力
Golang 生态工具与框架:扩展开发能力

《Golang 生态工具与框架》系统梳理 Go 语言在实际工程中的主流工具链与框架选型思路,涵盖 Web 框架、RPC 通信、依赖管理、测试工具、代码生成与项目结构设计等内容。通过真实项目场景解析不同工具的适用边界与组合方式,帮助开发者构建高效、可维护的 Go 工程体系,并提升团队协作与交付效率。

1

2026.02.24

Golang 性能优化专题:提升应用效率
Golang 性能优化专题:提升应用效率

《Golang 性能优化专题》聚焦 Go 应用在高并发与大规模服务中的性能问题,从 profiling、内存分配、Goroutine 调度、GC 机制到 I/O 与锁竞争逐层分析。结合真实案例讲解定位瓶颈的方法与优化策略,帮助开发者建立系统化性能调优思维,在保证代码可维护性的同时显著提升服务吞吐与稳定性。

2

2026.02.24

Golang 面试题精选:高频问题与解答
Golang 面试题精选:高频问题与解答

Golang 面试题精选》系统整理企业常见 Go 技术面试问题,覆盖语言基础、并发模型、内存与调度机制、网络编程、工程实践与性能优化等核心知识点。每道题不仅给出答案,还拆解背后的设计原理与考察思路,帮助读者建立完整知识结构,在面试与实际开发中都能更从容应对复杂问题。

1

2026.02.24

Golang 运行与部署实战:从本地到云端
Golang 运行与部署实战:从本地到云端

《Golang 运行与部署实战》围绕 Go 应用从开发完成到稳定上线的完整流程展开,系统讲解编译构建、环境配置、日志与配置管理、容器化部署以及常见运维问题处理。结合真实项目场景,拆解自动化构建与持续部署思路,帮助开发者建立可靠的发布流程,提升服务稳定性与可维护性。

3

2026.02.24

Golang 疑难杂症解决指南:常见问题排查与优化
Golang 疑难杂症解决指南:常见问题排查与优化

《Golang 疑难杂症解决指南》聚焦开发过程中常见却棘手的问题,从并发模型、内存管理、性能瓶颈到工程化实践逐步拆解。通过真实案例与调试思路,帮助开发者定位问题根因,建立系统化排查方法。不只给出答案,更强调分析路径与工具使用,让你在复杂 Go 项目中具备持续解决问题的能力。

1

2026.02.24

Golang 入门学习路线:从零基础到上手开发
Golang 入门学习路线:从零基础到上手开发

Golang 入门路线涵盖从零到上手的核心路径:首先打牢基础语法与切片等底层机制;随后攻克 Go 的灵魂——接口设计与 Goroutine 并发模型;接着通过 Gin 框架与 GORM 深入 Web 开发实战;最后在微服务与云原生工具开发中进阶,旨在培养具备高性能并发处理能力的后端工程师。

0

2026.02.24

中国研究生招生信息网官方网站入口 研招网网页版在线入口
中国研究生招生信息网官方网站入口 研招网网页版在线入口

中国研究生招生信息网入口(https://yz.chsi.com.cn) 此网站是研究生报名入口的唯一官方网站

95

2026.02.24

苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法
苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法

本专题汇总苹果官网最新可用入口及中国站点访问方式,涵盖官网直达链接、iPhone官方页面查看方法与常见访问说明,帮助用户快速进入苹果官方网站,便捷了解产品信息与官方服务。

14

2026.02.24

Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址
Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址

本专题系统整理Asianfanfics(AFF)官方网站最新可用入口,涵盖官方平台最新直达地址、官网登录方式及中文访问指引,帮助用户快速、安全地进入AFF平台浏览与使用相关内容。

15

2026.02.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3.9万人学习

C# 教程
C# 教程

共94课时 | 10.2万人学习

Java 教程
Java 教程

共578课时 | 71.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号