我研究了一下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;