我研究了一下jdk的实现,但愿对你有所帮助,研究状况以下: html
在我本机测试代码,查看序列化的文件guo.txt,在ultraEdit下, 用本地编码看会是一串乱码,可是用十六进制查看,就能够发现规律,文件内容以下: AC ED 00 05 7A 00 00 02 FD 11 00 0A 0D 00 0A 07.....(后面内容省略) 每次执行,发现前面的AC ED 00 05总会存在。先解释这个吧。 我在此只是想以代码进一步证实: 对于建立一个对象输出流时,查看构造器的代码以下: java
Java代码api
public ObjectOutputStream(OutputStream out) throws IOException { verifySubclass(); bout = new BlockDataOutputStream(out); handles = new HandleTable(10, (float) 3.00); subs = new ReplaceTable(10, (float) 3.00); enableOverride = false; writeStreamHeader(); bout.setBlockDataMode(true); if (extendedDebugInfo) { debugInfoStack = new DebugTraceInfoStack(); } else { debugInfoStack = null; } } writeStreamHeader(); //这句很重要,即只要你针对文件打开一个对象输出流,它就会向其中写入4个字节的内容。能够查看到写入的内容是:
Java代码数组
protected void writeStreamHeader() throws IOException { bout.writeShort(STREAM_MAGIC); bout.writeShort(STREAM_VERSION); }
Java代码缓存
public interface ObjectStreamConstants { /** * Magic number that is written to the stream header. */ final static short STREAM_MAGIC = (short)0xaced; /** * Version number that is written to the stream header. */ final static short STREAM_VERSION = 5;
从注释就能够知道,ACED只是写入序列化文件的一个固定标记,起标识做用。 oracle
BlockDataOutputStream
类的writeShort
方法在内部又使用了Bits
类的方法,具体感兴趣的同窗能够本身查看源代码。因为写进的是short,在java中short占两个字节,因此文件头就变成了AC ED 00 05。而后解释7A。其实当你测试比较少的数据时,当写入的数据长度小于255个时,文件的前面部分会有另一种结果: AC ED 00 05 77 05 01 10 13 0A 06 ide
bout
是OjbectOutStream
的私有静态内部类BlockDataOutputStream
的实例。 该类持有 测试
Java代码this
/** buffer for writing block data headers */ private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; // 缓存数据块的头部信息 /** buffer for writing general/block data */ private final byte[] buf = new byte[MAX_BLOCK_SIZE]; // 该缓冲数组保存要写入的数据,但长度大于MAX_BLOCK_SIZE(1024)时,会刷新缓冲将内容写入文件。
在api中也清楚的注明,摘录入下: 编码
serializable
字段和 externalizable
数据)以块数据记录的形式写入 ObjectOutputStream
中。块数据记录由头部和数据组成。 块数据部分包括标记和跟在部分后面的字节数。连续的基本写入数据被合并在一个块数据记录中。块数据记录的分块因子为 1024 字节。每一个块数据记录都将填满 1024 字节,或者在终止块数据模式时被写入。源码以下:
Java代码
private void writeBlockHeader(int len) throws IOException { if (len <= 0xFF) { hbuf[0] = TC_BLOCKDATA; hbuf[1] = (byte) len; out.write(hbuf, 0, 2); } else { hbuf[0] = TC_BLOCKDATALONG; Bits.putInt(hbuf, 1, len); out.write(hbuf, 0, 5); } }
以上代码中的TC_BLOCKDATA也定义在接口ObjectStreamConstants中。
Java代码
/** * Block of optional data. Byte following tag indicates number * of bytes in this block data. */ final static byte TC_BLOCKDATA = (byte) 0x77; // 跟在77后面的字节记录一个数据块中实际写入的字节数,这里只有5个字节的数据内容,因为只有一个字节记录,因此最多只能有 255个字节的数据部分,数据长度该句代码hbuf[1] = (byte) len;产生。 /** * long Block data. The long following the tag indicates the * number of bytes in this block data. */ final static byte TC_BLOCKDATALONG= (byte)0x7A; // 一样的后面跟的四个字节来记录写入的数据长度
Bits.putInt(hbuf, 1, len);
继续查看类Bits的该方法:
Java代码
static void putInt(byte[] b, int off, int val) { b[off + 3] = (byte) (val >>> 0); b[off + 2] = (byte) (val >>> 8); b[off + 1] = (byte) (val >>> 16); b[off + 0] = (byte) (val >>> 24); }
该方法相信你们应该看得懂,其实就是把长度(int)
的每一个字节取出放到了hbuf
中和7A
一块儿作头部。
还有一点就是,若是你调用输出流的write
方法后,却不去关闭输出流,当数据量小于1024个字节时,文件中只会包含序列化的文件头: AC ED 00 05,而没有真正写入其余数据.数据量大于1024个字节的话,因为超过容量会刷新缓冲区,文件固然就包含数据咯。因此咱们在写入较少数据的时候,注意要关闭输出流,这样就能够在关闭时,将缓冲区的数据写入到文件中去。
还有部分非关键代码就不贴出来了,想研究的同窗直接查看就是了。以上内容只是本人针对源码结合测试得出的结论,不足之处,还请你们批评指正。
针对对象序列化,会将序列化的类和字段的基本信息保存在序列化文件中,也能够想见反序列化总会依赖必定信息吧,不可能直接针对一个普通文本文件就反序列化,这样就欠考虑了,呵呵。它也会首先根据文件头(即AC ED 00 05)来断定是否是一个序列化文件,若是不是就直接抛出异常。
Serialiazble
接口时,序列化后再次反序列化时,不会调用该类的默认构造器。 Externalizable
接口时,在反序列化产生实例时,会调用默认构造器,初始化成员变量。The following symbols in java.io.ObjectStreamConstants
define the terminal and constant values expected in a stream.
final static short STREAM_MAGIC = (short)0xaced; final static short STREAM_VERSION = 5; final static byte TC_NULL = (byte)0x70; final static byte TC_REFERENCE = (byte)0x71; final static byte TC_CLASSDESC = (byte)0x72; final static byte TC_OBJECT = (byte)0x73; final static byte TC_STRING = (byte)0x74; final static byte TC_ARRAY = (byte)0x75; final static byte TC_CLASS = (byte)0x76; final static byte TC_BLOCKDATA = (byte)0x77; final static byte TC_ENDBLOCKDATA = (byte)0x78; final static byte TC_RESET = (byte)0x79; final static byte TC_BLOCKDATALONG = (byte)0x7A; final static byte TC_EXCEPTION = (byte)0x7B; final static byte TC_LONGSTRING = (byte) 0x7C; final static byte TC_PROXYCLASSDESC = (byte) 0x7D; final static byte TC_ENUM = (byte) 0x7E; final static int baseWireHandle = 0x7E0000;
The flag byte classDescFlags may include values of
final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE final static byte SC_SERIALIZABLE = 0x02; final static byte SC_EXTERNALIZABLE = 0x04; final static byte SC_ENUM = 0x10;