对象的输入输出-Java序列化机制

对象序列化和反序列化,在Java中体现为两种字节流: ObjectInputStreamObjectOutputStream

序列化的概念

指堆内存中的java对象数据,通过某种方式存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。

反序列化,顾名思义,则是将二进制字节流转化回java对象的过程。

java序列化是平台无关的

序列化的作用

  1. 对象持久化
  2. 网络传输对象
  3. 进程间传递对象

如何序列化

默认序列化

  1. 为类实现一个Serializable接口,这是一个空接口,没有需要实现的方法。
  2. 可以调用ObjectOutputStream.writeObject方法来序列化了。
  3. 调用ObjectInputStream.readObject来反序列化

自定义序列化过程

实现Serializable接口之后,重写 writeObject(ObjectOutputStream) 、 readObject(ObjectInputStream) 方法。

序列化ID

通常可序列化的类会使用一个序列化ID:

private static final long serialVersionUID = 1L;

java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常InvalidClassException

序列化之后的字节流格式

我们用这样一段代码做实验:

class SerializableObject implements Serializable{ }

public class Main {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(System.out);
        Object obj = new SerializableObject();
        out.writeObject(obj);
    }
}

得到的结果为:

aced 0005 7372 0012 5365 7269 616c 697a
6162 6c65 4f62 6a65 6374 0b8e b091 7d68
738d 0200 0078 70

前两个字节固定: ac ed

紧接的是对象序列化格式的版本号: 00 05

接下来,73 代表接下来读取到的将是一个对象,72 代表该对象是一个对类的描述:73 72

接下来的两字节描述类名长度:00 12,对应18个字节的全限定类名。

于是接下来的18字节就是类名的UTF编码:5365 7269 616c 697a 6162 6c65 4f62 6a65 6374,就是类名SerializableObject。

接下来八个字节是序列化版本ID:0b8e b091 7d68 738d

接下来一个字节02代表了序列化中标识类版本。

继续往下两个字节就是 00 00 , 代表该类中字段的个数,由于我这个类内部为空没有字段,所以这里就是0。

然后是它包含的字段序列类型描述,其顺序即它们存储的顺序.

基本类型的组成为:一字节类型描述符 两字节字段名称长度 字段名称

类型描述符:

解码字符 代表类型
B byte
C char
D double
F float
I int
J long
L 对象
S short
Z boolean
[ 数组

字段序列类型描述结束后附加两个字节是固定的78 70

最后是字段信息(内容)的描述,这里都没有。

特殊情况

  1. 静态变量不会被序列化(static),transient则可以限定字段不参与序列化。
  2. 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
  3. 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e5%af%b9%e8%b1%a1%e7%9a%84%e8%be%93%e5%85%a5%e8%be%93%e5%87%ba-java%e5%ba%8f%e5%88%97%e5%8c%96%e6%9c%ba%e5%88%b6/

(0)
彭晨涛彭晨涛管理者
上一篇 2019年12月21日
下一篇 2019年12月22日

相关推荐

  • Collection接口研究

    以下内容基于jdk1.8 接口Collection分析 img 该接口实现了接口Iterable 方法: int size(); 返回元素的个数 boolean isEmpty()…

    2019年11月13日
    0100
  • 深入理解java虚拟机第三版读书笔记03

    续深入理解java虚拟机第三版读书笔记02 HotSpot虚拟机对象探秘 对象的创建 当Java虚拟机遇到一条字节码new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一…

    2020年1月6日
    0130
  • CountDownLatch及CyclicBarrier源码分析

    之前写的一篇博客JUC包下的线程协作计数CountDownLatch及CyclicBarrier只是介绍了一下这两个工具类的用法,并没有深入探究源码,然而实现方法也比较简单,所以合…

    Java 2020年5月22日
    0140
  • Java反射机制和动态代理详解

    反射 概念 反射机制: 指的是可以于运行时加载、探知、使用编译期间完全未知的类。 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性…

    Java 2020年2月15日
    0570
  • 深入理解java虚拟机第三版读书笔记02

    以下是第二章 Java内存区域与内存溢出异常的内容 运行时数据区域 程序计数器 特点 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。 程序计数器是…

    2020年1月5日
    0200
  • HashMap源码分析

    HashMap是java中非常常见的一个数据结构,在这篇文章里,我依然以Map中的操作为导向来循序渐进研究HashMap中的源码,阅读这篇文章需要的前置知识有: 弱平衡的二叉查找树…

    Java 2020年2月12日
    0220
  • Java线程池详解

    线程池就是享元模式和生产者消费者模式的应用 动手实现线程池 步骤1:自定义拒绝策略接口 @FunctionalInterface // 拒绝策略 interface RejectP…

    2020年2月3日
    0310
  • 深入理解java虚拟机第三版读书笔记04

    以下是第三章 垃圾收集器与内存分配策略的内容 概述 程序计数器、虚拟机栈、本地方法栈是线程独有的,栈帧更是随方法结束而消亡,不需要垃圾回收。而堆和方法区则需要经过垃圾回收的设计 对…

    2020年1月8日
    0290
  • Java日志框架LOG4J2的介绍和使用

    前置知识: 日志门面SLF4J介绍和使用 推荐阅读: Java日志框架Logback介绍和使用 log4j2概述 Apache Log4j2是对Log4j的升级版,参考了logba…

    中间件/工具/框架 2020年3月10日
    02350
  • 快速失败(fail-fast)和安全失败(fail-safe)

    快速失败 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出 Concurrent Modification Exception。…

    Java 2020年2月23日
    0140

发表回复

登录后才能评论