1. 了解序列化的意义

Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在, 即,这些对象的生命周期不会比 JVM 的生命周期更长。但在现实应用中,就可能要求在 JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能

序列化:是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化

反序列化:是序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化

2.序列化面临的挑战

评价一个序列化算法优劣的两个重要指标是:序列化以后的数据大小;
序列化操作本身的速度及系统资源开销(CPU、内存);

java 语言本身提供了对象序列化机制,也是 Java 语言本身最重要的底层机制之一,Java本身提供的序列化机制存在两个问题

  • 序列化的数据比较大,传输效率低

  • 其他语言无法识别和对接

基于JDK序列化方式实现
JDK 提供了 Java 对 象 的 序 列 化 方 式 , 主 要 通 过 输 出 流java.io.ObjectOutputStream 和对象输入流 java.io.ObjectInputStream来实现。其中,被序列化的对象需要实现java.io.Serializable接口

提供序列化接口

/** * @Project: 3.DistributedProject * @description:  序列化接口 * @author: sunkang * @create: 2018-06-28 19:30 * @ModificationHistory who      when       What **/public interface ISerializer {     byte[] serializer(T obj);     T  deSerailizer(byte[] bytes,Class clazz);}

email 实体类

/** * @Project: 3.DistributedProject * @description:  email 实体类 * @author: sunkang * @create: 2018-06-30 14:23 * @ModificationHistory who      when       What **/public class Email implements Serializable {    private String content;    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }    @Override    public String toString() {        return "Email{" +                "content='" + content + '\'' +                '}';    }}

person实体类

/** * @Project: 3.DistributedProject * @description:   person实体类 * @author: sunkang * @create: 2018-06-28 19:44 * @ModificationHistory who      when       What **/public class Person implements Serializable,Cloneable {    private  String name ;    private  int age;    private  String sex;    private Email email;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public Email getEmail() {        return email;    }    public void setEmail(Email email) {        this.email = email;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                ", sex='" + sex + '\'' +                ", email=" + email +                '}';    }}

java序列化

/** * @Project: 3.DistributedProject * @description:  java序列化的方式 * @author: sunkang * @create: 2018-06-28 19:32 * @ModificationHistory who      when       What **/public class JavaSerializer implements ISerializer {    @Override    public  byte[] serializer(T obj)  {        ObjectOutputStream  oos = null;        ByteArrayOutputStream byteArrayOutputStream= null;        try {              byteArrayOutputStream =  new ByteArrayOutputStream();              oos = new ObjectOutputStream(byteArrayOutputStream);              oos.writeObject(obj);             return byteArrayOutputStream.toByteArray();        } catch (IOException e) {            e.printStackTrace();        }finally {            if(oos!=null){                try {                    oos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(byteArrayOutputStream != null){                try {                    byteArrayOutputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return null;    }    @Override    public  T deSerailizer(byte[] bytes, Class clazz) {        ByteArrayInputStream byteArrayInputStream = null;        ObjectInputStream  objectInputStream = null;        try {             byteArrayInputStream  = new ByteArrayInputStream(bytes);            objectInputStream =   new ObjectInputStream(byteArrayInputStream);            return  (T)objectInputStream.readObject();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }finally {            if(byteArrayInputStream!=null){                try {                    byteArrayInputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(objectInputStream != null){                try {                    objectInputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return null;    }}

测试类:

public class App {    public static void main( String[] args ){        //java 序列化        ISerializer serializer=  new JavaSerializer();        Person person = new Person();        person.setName("sunkang");        person.setAge(23);        person.setSex("nan");        Email email = new Email();        email.setContent("我的邮件");        person.setEmail(email);        byte[] javaBytes =serializer.serializer(person);        Person  person1= serializer.deSerailizer(javaBytes,Person.class);        System.out.println(person1.toString());}}

输出结果

Person{name='sunkang', age=23, sex='nan', email=Email{content='我的邮件'}}
3.序列化的高阶认识
  • serialVersionUID的作用
    Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException

    如果没有为指定的 class 配置 serialVersionUID,那么 java 编译器会自动给这个 class 进行一个摘要算法,类似于指纹算法, 只要这个文件有任何改动,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的

  • serialVersionUID 有两种显示的生成方式
    一是默认的 1L ,比 如:private static final long serialVersionUID = 1L;
    二是根据类名、接口名、成员方法及属性等来生成一个64 位的哈希字段

当实现 java.io.Serializable 接 口 的 类 没 有 显 式 地 定 义 一 个serialVersionUID 变量时候,Java 序列化机制会根据编译的 Class 自动生成一个 serialVersionUID 作序列化版本比较用,这种情况下,如果Class 文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID 也不会变化的。

  • 静态变量序列化
    序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

  • 父类的序列化
    1.子 类 继 承 该 父 类 并 且 实 现 了 序 列 化,此时父类并没有实现序列化,在反序列化该子类后,是没办法获取到父类的属性值的
    2.当一个父类实现序列化,子类自动实现序列化,不需要再显示实现Serializable 接口
    3.当一个对象的实例变量引用了其他对象,序列化该对象时 也会把引用对象进行序列化,但是前提是该引用对象必须实现序列化接口

  • Transient 关键字
    控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值被设为初始值, 如 int 型的是 0,对象型的是 null

  • 绕开 transient 机制的办法
    实体需要实现writeObject(ObjectOutputStream obj)
    readObject( ObjectInputStream objectInputStream ))的方法
    比如 hobby还是要序列化,需要手动实现这两个方法:

 private transient  String hobby;    private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {        objectInputStream.defaultReadObject();        this.hobby = (String) objectInputStream.readObject();    }    private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {        objectOutputStream.defaultWriteObject();        objectOutputStream.writeObject(this.hobby);    }


writeObject和 readObject 这两个私有的方法,既不属于 Object、也不是 Serializable,为什么能够在序列化的时候被调用呢?
原因是:ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为 ObjectOutputStream使用 getPrivateMethod,所以这些方法必须声明为 priate 以至于供 ObjectOutputStream 来使用

  • 同一对象两次写入

/** * @Project: serializer-demo * @description:  同一对象存储两次 * @author: sunkang * @create: 2018-11-11 20:59 * @ModificationHistory who      when       What **/public class StroreRuleDemo {    public static void main(String[] args) throws IOException {        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.txt"));        Person person = new Person();        person.setName("sunkang");        person.setAge(23);        person.setSex("nan");        Email email = new Email();        email.setContent("我的邮件");        person.setEmail(email);        //第一次写入        objectOutputStream.writeObject(person);        objectOutputStream.flush();        System.out.println(new File("user.txt").length());        //第二次写入同一个对象        objectOutputStream.writeObject(person);        objectOutputStream.close();        System.out.println(new File("user.txt").length());    }}

输出结果:可以发现第二次写入对象时文件只增加了 5 字节

230235

Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系.该存储规则极大的节省了存储空间

4.序列化实现深克隆

在 Java 中存在一个 Cloneable 接口,通过实现这个接口的类都会具备clone 的能力,
同时 clone 是在内存中进行,在性能方面会比我们直接通过 new 生成对象要高一些,特别是一些大的对象的生成,性能提升相对比较明显。那么在Java领域中,克隆分为深度克隆和浅克隆

  • 浅克隆:  被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象

  • 深克隆:  被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍

5.常见的序列化技术

  • 使用JAVA进行序列化
    优点:JAVA语言本身提供,使用比较方便和简单
    缺点:不支持跨语言处理、 性能相对不是很好,序列化以后产生的数据相对较大
    实现方式,前面的已经展示过了

  • XML 序列化框架
    XML序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能不高,而且 QPS 较低的企业级内部系统之间的数据交换的场景,同时 XML 又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的Webservice,就是采用XML格式对数据进行序列化的

先引用maven依赖,引用xstream的包完成xml到javabean的映射

          com.thoughtworks.xstream      xstream      1.4.10    

使用xstream 实现xml的序列化

/** * @Project: 3.DistributedProject * @description:   xml实现序列化 * @author: sunkang * @create: 2018-06-28 22:54 * @ModificationHistory who      when       What **/public class xmlSerializer implements  ISerializer {    XStream xStream = new XStream(new DomDriver());    @Override    public  byte[] serializer(T obj) {        return xStream.toXML(obj).getBytes();    }    @Override    public  T deSerailizer(byte[] bytes, Class clazz) {        return (T)xStream.fromXML(new String(bytes));    }}
  • JSON 序列化框架
    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML来说,JSON的字节流更小,而且可读性也非常好。现在JSON数据格式在企业运用是最普遍的
    JSON序列化常用的开源工具有很多1. Jackson(https://github.com/FasterXML/jackson)2. 阿里开源的 FastJson(https://github.com/alibaba/fastjon)3. Google 的 GSON (https://github.com/google/gson)
    这几种 json 序列化工具中,Jackson 与 fastjson 要比 GSON 的性能要好,但是 Jackson、GSON 的稳定性要比 Fastjson 好。而 fastjson 的优势在于提供的 api 非常容易使用

先引用maven依赖

          com.alibaba      fastjson      1.2.29    

fastjson实现序列化

/** * @Project: 3.DistributedProject * @description:  fastjson序列化 * @author: sunkang * @create: 2018-06-28 22:44 * @ModificationHistory who      when       What **/public class FastJsonSerializer implements ISerializer {    @Override    public  byte[] serializer(T obj) {        return JSON.toJSONString(obj).getBytes();    }    @Override    public  T deSerailizer(byte[] bytes, Class clazz) {        return JSON.parseObject(new String(bytes),clazz);    }}
  • Hessian序列化框架
    Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说, Hessian 具有更好的性能和易用性,而且支持多种不同的语言,实际上 Dubbo 采用的就是 Hessian 序列化来实现,只不过 Dubbo 对Hessian进行了重构,性能更高

  • Protobuf 序列化框架
    Protobuf是Google的一种数据交换格式,它独立于语言、独立于平台。
    Google提供了多种语言来实现,比如Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件
    Protobuf 使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的 RPC 调用。另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中但是但是要使用 Protobuf 会相对来说麻烦些,因为他有自己的语法,有自己的编译器 ,但是但是要使用 Protobuf 会相对来说麻烦些,因为他有自己的语法,有自己的编译器

1)下载 protobuf 工具
https://github.com/google/protobuf/releases 找到 protoc-3.5.1-win32.zip
2)编写 proto 文件

syntax="proto2";package com.gupaoedu.serial;option java_package = "com.gupaoedu.serial";option java_outer_classname="UserProtos";message User {required string name=1;required int32 age=2;}

3)proto 的语法
包名
option 选项
消息模型(消息对象、字段(字段修饰符-required/optional/repeated)
字段类型(基本数据类型、枚举、消息对象)、字段名、标识号)
4)生成实体类
在 protoc.exe 安装目录下执行如下命令

.\protoc.exe --java_out=./ ./user.proto
5)运行查看结果将生成以后的 UserProto.java 拷贝到项目中

怎么使用protobuf可以先参考:

https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html

6.Protobuf 原理

如果后面有机会用到protobuf,原理我会后面进行补充。

序列化与反序列化_分布式系统基础之序列化和反序列化相关推荐

  1. java序列化的方法_【Java常见序列化与反序列方法总结】

    人和电脑在很多方面都是十分相似的,大脑可以看成电脑主机,五官/身体等表面器官就是显示器.鼠标等外设.这篇文章就是想把计算机跟人做类比YY一下序列化和反序列化的机制.用途. 如果你是初学者,心里肯定会问 ...

  2. java序列化如何实现_什么是java序列化,如何实现java序列化

    Java串行化技术可以使你将一个对象的状态写入一个Byte流里,并且可以从其它地方把该Byte流里的数据读出来,重新构造一个相同的对象.这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数 ...

  3. 序列化加密字段_自动加密可序列化的类

    序列化加密字段 在Coursera安全顶峰项目的验尸讨论中提出了一个疯狂的想法. 类可以在序列化期间自行加密吗? 这主要是学术上的"假设"练习. 很难想到这样一种情况,我们希望在持 ...

  4. 序列化 自定义名字_反序列化漏洞(Web漏洞及防御)

    为了有效地存储或传递数据,同时不丢失其类型和结构,经常需要利用序列化和反序列化函数对数据进行处理! 序列化 将对象或数据转换为可以恢复的数据格式的过程 什么时候需要序列化? 1)对象在网络上传输时 2 ...

  5. oracle存储过程如何传递一个bean对象_对象传输,序列化和反序列化

    一.前言 二.基本概念 2.1 序列化和反序列化的定义及最重要的作用 序列化的定义:Java序列化就是指把Java对象转换为字节序列的过程: 反序列化的定义:Java反序列化就是指把字节序列恢复为Ja ...

  6. spidermonkey php,javascript SpiderMonkey中的函数序列化如何进行_基础知识

    在Javascript中,函数可以很容易的被序列化(字符串化),也就是得到函数的源码.但其实这个操作的内部实现(引擎实现)并不是你想象的那么简单.SpiderMonkey中一共使用过两种函数序列化的技 ...

  7. java string 序列化_详解JAVA序列化

    享学课堂作者:逐梦々少年 转载请声明出处! 现在开发过程中经常遇到多个进程多个服务间需要交互,或者不同语言的服务之间需要交互,这个时候,我们一般选择使用固定的协议,将数据传输过去,但是在很多语言,比如 ...

  8. sql和泛型方法返回泛型_基于泛型编程的序列化实现方法

    写在前面 序列化是一个转储-恢复的操作过程,即支持将一个对象转储到临时缓冲或者永久文件中和恢复临时缓冲或者永久文件中的内容到一个对象中等操作,其目的是可以在不同的应用程序之间共享和传输数据,以达到跨应 ...

  9. java基础----IO序列化Serializable

    package com.henu.io; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

最新文章

  1. 如何免费使用数据挖掘软件RapidMiner - 申请学生许可证
  2. Python 数据分析与展示笔记2 -- 图像手绘效果
  3. css不常用重要属性
  4. 常用JQuery插件整理
  5. 深入理解 Java 内存模型(一)——基础
  6. lodash 源码解读 _.findIndex(obj_array, fn)
  7. 体验Visual Studio 2015 Windows Forms应用程序开发与维护
  8. 通过镜像快速搭建本地yum源
  9. 国内首个基于liftweb开发的项目上线运营
  10. Java swing 开发windows桌面应用:课堂点名程序
  11. python学习笔记6---数据解析
  12. 手机英文上面的逗号怎么打_英文中关于逗号使用方法
  13. [Hi3751V350][Android9.0] 调试笔记 --- 添加并设置默认系统字库
  14. C#工作总结(一):Fleck的WebSocket使用
  15. R语言必看推荐:R语言入门经典版(中文版)+R语言实战第二版(中文完整版)
  16. 水下导弹发射环境因素建模需求及其扫盲笔记
  17. 海康威视启用码流加密_如何在Windows 10上启用全盘加密
  18. 如何将 bn.bing.com 重定向至 www.bing.com
  19. 闲话 | 人生,是一场怎样的修行
  20. 谷歌硬盘,百度云盘等超大文件利用IDM加速下载,防止限流方法

热门文章

  1. osgb转obj工具_在ArcGIS Pro中OSGB数据转换及发布服务流程
  2. HTML中轮播图里的圆点不充满,老师好,轮播图中的小点不显示
  3. 安装mysql最后一步第二个打叉_Mysql安装到最后一步时。出现start service红叉   亲朋好友帮忙指导一下!谢谢! 百...
  4. gui python qt 绘图_最全整理!计算、可视化、机器学习等8大领域38个常用Python库...
  5. 裸板烧写linux内核,嵌入式linux学习(二):烧写裸板进程
  6. Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf(五)
  7. python 多列对齐_python – 如何连接两个数据帧并在这样的特定列上对齐?
  8. 通过负载均衡器+域名实现容灾切换-(9)GSLB典型产品-F5简介
  9. tensorflow--filter、strides
  10. 洛谷P5269 欧稳欧再次学车