媒介
对付Java的序列化,一直只知道只需要实现Serializbale这个接口就可以了,详细内部实现一直不是很相识,正好这次在反复造RPC的轮子的时候涉及到序列化问题,就抽时间看了下 Java序列化的底层实现,这篇文章算是这次的进修小结吧。
第一部门:What
Java序列化是指把Java工具生存为二进制字节码的进程,Java反序列化是指把二进制码从头转换成Java工具的进程。
那么为什么需要序列化呢?
第一种环境是:一般环境下Java工具的声明周期都比Java虚拟机的要短,实际应用中我们但愿在JVM遏制运行之后可以或许耐久化指定的工具,这时候就需要把工具举办序列化之后生存。
第二种环境是:需要把Java工具通过网络举办传输的时候。因为数据只可以或许以二进制的形式在网络中举办传输,因此当把工具通过网络发送出去之前需要先序列化成二进制数据,在吸收端读到二进制数据之后反序列化成Java工具。
第二部门:How
本部门以序列化到文件为例讲授Java序列化的根基用法。
package com.beautyboss.slogen; import java.io.*; /** * Author : Slogen * AddTime : 17/4/30 */ public class SerializableTest { public static void main(String[] args) throws Exception { FileOutputStream fos = new FileOutputStream("temp.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); TestObject testObject = new TestObject(); oos.writeObject(testObject); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("temp.out"); ObjectInputStream ois = new ObjectInputStream(fis); TestObject deTest = (TestObject) ois.readObject(); System.out.println(deTest.testValue); System.out.println(deTest.parentValue); System.out.println(deTest.innerObject.innerValue); } } class Parent implements Serializable { private static final long serialVersionUID = -4963266899668807475L; public int parentValue = 100; } class InnerObject implements Serializable { private static final long serialVersionUID = 5704957411985783570L; public int innerValue = 200; } class TestObject extends Parent implements Serializable { private static final long serialVersionUID = -3186721026267206914L; public int testValue = 300; public InnerObject innerObject = new InnerObject(); }
措施执行完用vim打开temp.out文件,可以看到
0000000: aced 0005 7372 0020 636f 6d2e 6265 6175 ....sr. com.beau 0000010: 7479 626f 7373 2e73 6c6f 6765 6e2e 5465 tyboss.slogen.Te 0000020: 7374 4f62 6a65 6374 d3c6 7e1c 4f13 2afe stObject..~.O.*. 0000030: 0200 0249 0009 7465 7374 5661 6c75 654c ...I..testValueL 0000040: 000b 696e 6e65 724f 626a 6563 7474 0023 ..innerObjectt.# 0000050: 4c63 6f6d 2f62 6561 7574 7962 6f73 732f Lcom/beautyboss/ 0000060: 736c 6f67 656e 2f49 6e6e 6572 4f62 6a65 slogen/InnerObje 0000070: 6374 3b78 7200 1c63 6f6d 2e62 6561 7574 ct;xr..com.beaut 0000080: 7962 6f73 732e 736c 6f67 656e 2e50 6172 yboss.slogen.Par 0000090: 656e 74bb 1eef 0d1f c950 cd02 0001 4900 ent......P....I. 00000a0: 0b70 6172 656e 7456 616c 7565 7870 0000 .parentValuexp.. 00000b0: 0064 0000 012c 7372 0021 636f 6d2e 6265 .d...,sr.!com.be 00000c0: 6175 7479 626f 7373 2e73 6c6f 6765 6e2e autyboss.slogen. 00000d0: 496e 6e65 724f 626a 6563 744f 2c14 8a40 InnerObjectO,..@ 00000e0: 24fb 1202 0001 4900 0a69 6e6e 6572 5661 $.....I..innerVa 00000f0: 6c75 6578 7000 0000 c8 luexp....
第三部门:Why
挪用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()之后毕竟做了什么?temp.out文件中的二进制别离代表什么意思?
别急,软件开发,且听我娓娓道来。
1. ObjectStreamClass类
官方文档对这个类的先容如下
Serialization’s descriptor for classes. It contains the name and serialVersionUID of the class. The ObjectStreamClass for a specific class loaded in this Java VM can be found/created using the lookup method.
可以看到ObjectStreamClass这个是类的序列化描写符,这个类可以描写需要被序列化的类的元数据,包罗被序列化的类的名字以及序列号。可以通过lookup()要领来查找/建设在这个JVM中加载的特定的ObjectStreamClass工具。
2. 序列化:writeObject()
在挪用wroteObject()举办序列化之前会先挪用ObjectOutputStream的结构函数生成一个ObjectOutputStream工具,结构函数如下:
public ObjectOutputStream(OutputStream out) throws IOException { verifySubclass(); // bout暗示底层的字节数据容器 bout = new BlockDataOutputStream(out); handles = new HandleTable(10, (float) 3.00); subs = new ReplaceTable(10, (float) 3.00); enableOverride = false; writeStreamHeader(); // 写入文件头 bout.setBlockDataMode(true); // flush数据 if (extendedDebugInfo) { debugInfoStack = new DebugTraceInfoStack(); } else { debugInfoStack = null; } }