Java序列化

前言

序列化並非Java語言獨有的機制,它表示的是將一個對象的狀態信息轉換成可傳輸或可存儲的數據格式的過程,當須要再次使用該對象時經過反序列化將對象還原。java

例如在有些場景下咱們可能須要將Java對象傳輸給網絡另外一端的JVM上使用,像調用RPC方法傳遞參數對象,或者有時咱們但願Java對象的生命週期能比JVM長,即便JVM中止運行了下一次重啓也能繼續使用,這時候能夠經過將Java對象序列化後保存到文件系統,下次須要使用時經過反序列化還原成以前的Java對象。數組

在平常開發中咱們常常接觸到的序列化主要有基於XML數據格式的序列化、基於JSON格式的序列化和Java原始的序列化,本文只討論Java原生支持的序列化。安全

基本用法

要將Java對象序列化爲二進制字節碼,被序列化的對象必須實現java.io.Serializable接口或者java.io.Externalizable接口。網絡

java.io.Serializable是一個空接口,僅起到一個標識做用,標識該對象能夠被序列化,當對實現了該接口的對象進行序列化時,會使用Java默認的序列化方式進行序列化。ide

java.io.Externalizable接口則是Java提供的一種支持自定義序列化的方法,實現該接口必須由開發者提供void readExternal(ObjectInput in)void writeExternal(ObjectOutput out)兩個方法的實現。函數

若是沒有實現這兩個接口中的其中一個,那麼在序列化時會拋出java.io.NotSerializableException源碼分析

一般實現Serializable接口是最簡單快速支持序列化的方案,能夠經過ObjectOutputStream.writeObject(Object obj)方法將參數指定的obj對象序列化爲二進制字節碼,並把獲得的字節碼寫入一個目標輸出流,例如FileOutputStream中。而使用ObjectInputStream.readObject()方法則能夠從一個源輸入流中讀取字節序列,反序列化爲Java對象返回。佈局

代碼示例

  • 序列化對象Person實現Serializable
package com.liang;

import java.io.Serializable;

public class Person implements Serializable {

	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private transient String address;
	private Person parent;

	public Person(String name, int age, String address) {
		this.name = name;
		this.age = age;
		this.address = address;
	}

	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 Person getParent() {
		return parent;
	}

	public void setParent(Person parent) {
		this.parent = parent;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address + ", parent=" + parent + "]";
	}

}
  • 使用ObjectOutputStream/OutjectInputStream進行序列化、反序列化
package com.liang;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableTest {

	private static final String FILE_NAME = "temp.out";

	public static void main(String[] args) {
		// 序列化
		Person son = new Person("xiaoming", 10, "廣州");
		Person parent = new Person("daming", 40, "上海");
		son.setParent(parent);
		System.out.println("before serialization: " + son);
		try {
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
			oos.writeObject(son);
			oos.flush();
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 反序列化
		try {
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME));
			Person person = (Person) ois.readObject();
			System.out.println("after deserialization: " + person);
			ois.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}
}

程序運行結束,控制檯輸出:this

此處輸入圖片的描述

用UltraEdit或者MadEdit打開temp.out,結果顯示以下:編碼

序列化結果

幾點說明:

  • serialVersionUID常量的做用是爲了Java在反序列化過程當中保證對象的惟一性,若是序列化時的serialVersionUID與反序列化時使用的serialVersionUID不一致,將會拋出java.io.InvalidClassException異常。
  • 對象的成員屬性在序列化過程當中會被遞歸的序列化,因此若是成員屬性是引用類型也必須實現Serializable或Externalizab接口。
  • 除serialVersionUID外,對象中的靜態成員變量和transient關鍵字修飾的成員變量在序列化過程當中不會被序列化。

源碼分析

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));方法上打個斷點,跟蹤函數的調用過程和相關變量變化,相關源碼以下:

  • 實例化ObjectOutputSObjectOutputStream.new ObjectOutputStream(OutputStream out)
public ObjectOutputStream(OutputStream out) throws IOException {
    //進行安全檢測
    verifySubclass(); 
    
    //bout是BlockDataOutputStream實例,充當底層字節數據容器
    bout = new BlockDataOutputStream(out);
    
    //一個輕量級的哈希表,實現對象到整數值的映射
    handles = new HandleTable(10, (float) 3.00);
    
    /**
     * 一個輕量級的對象映射到替代對象的哈希表,
     * 內部由一個HandleTable示例和一個對象數組組成,
     * 經過HandlerTable實現對象到整數的映射,再經過整數從數組對象中返回替代對象
     */
    subs = new ReplaceTable(10, (float) 3.00);
    
    //若是爲true序列化時調用writeObjectOverride()方法,不然調用writeObject0()方法
    enableOverride = false;
    
    //寫入二進制流的頭部,包括「魔數」(0xACED)和版本(0x0005)
    writeStreamHeader();
    
    //設置blkmode屬性爲true
    bout.setBlockDataMode(true);
    if (extendedDebugInfo) {
        debugInfoStack = new DebugTraceInfoStack();
    } else {
        debugInfoStack = null;
    }
}
  • 序列化入口: ObjectOutputStream.writeObject(Object obj)
//若是enableOverride值爲true則調用writeObjectOverride(obj)方法,不然執行writeObject0(obj,false)方法
 public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {
        writeObjectOverride(obj);
        return;
    }
    try {
        writeObject0(obj, false);
    } catch (IOException ex) {
        if (depth == 0) {
            writeFatalException(ex);
        }
        throw ex;
    }
}
  • ObjectOutputStream.writeObject0(Object obj, boolean unshared)
private void writeObject0(Object obj, boolean unshared) throws IOException {
    boolean oldMode = bout.setBlockDataMode(false);
    depth++; //遞歸深度加1
    try {
        // handle previously written and non-replaceable objects
        int h;
        //obj爲null或者替代對象爲null時調用writeNull,寫入十六進制0x70
        if ((obj = subs.lookup(obj)) == null) { 
            writeNull();
            return;
        } 
        //
        else if (!unshared && (h = handles.lookup(obj)) != -1) {
            writeHandle(h);
            return;
        } 
        //待序列化的對象是java.lang.Class實例則執行writeClass,寫入0x7六、類描述符並把當前obj放到HandleTable裏
        else if (obj instanceof Class) { 
            writeClass((Class) obj, unshared);
            return;
        } 
        /** 
         * 待序列化的對象是ObjectStreamClass實例則執行writeClassDesc,寫入給定類的描述符,
         * ObjectStreamClass對象用來提取序列化過程當中某個對象所屬類的元數據信息,例如類名、 
         * 序列化號等等。
         */
        else if (obj instanceof ObjectStreamClass) { 
            writeClassDesc((ObjectStreamClass) obj, unshared);
            return;
        }
        
        //檢查是否須要對序列化的對象進行替換序列化
        Object orig = obj;
        Class<?> cl = obj.getClass();
        ObjectStreamClass desc;
        for (;;) {
            Class<?> repCl;
            desc = ObjectStreamClass.lookup(cl, true);
            if (!desc.hasWriteReplaceMethod() ||
                (obj = desc.invokeWriteReplace(obj)) == null ||
                (repCl = obj.getClass()) == cl)
            {
                break;
            }
            cl = repCl;
        }
        if (enableReplace) {
            Object rep = replaceObject(obj);
            if (rep != obj && rep != null) {
                cl = rep.getClass();
                desc = ObjectStreamClass.lookup(cl, true);
            }
            obj = rep;
        }

        // 若是序列化對象被替換過則須要進行與wirteObject0()開頭相似的一些判斷和處理
        if (obj != orig) {
            subs.assign(orig, obj);
            if (obj == null) {
                writeNull();
                return;
            } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                writeHandle(h);
                return;
            } else if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
                return;
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
                return;
            }
        }

        //根據對象的實際類型執行不一樣的寫入操做
        if (obj instanceof String) { //String對象序列化
            writeString((String) obj, unshared);
        } else if (cl.isArray()) { //數組對象序列化
            writeArray(obj, desc, unshared);
        } else if (obj instanceof Enum) { //枚舉對象序列化
            writeEnum((Enum<?>) obj, desc, unshared);
        } else if (obj instanceof Serializable) { //實現Serializable接口的對象序列化
            writeOrdinaryObject(obj, desc, unshared);
        } else { //不知足以上任一種狀況,拋出NotSerializableException
            if (extendedDebugInfo) {
                throw new NotSerializableException(
                    cl.getName() + "\n" + debugInfoStack.toString());
            } else {
                throw new NotSerializableException(cl.getName());
            }
        }
    } finally {
        depth--;
        bout.setBlockDataMode(oldMode);
    }
}
  • 序列化String對象: ObjectOutputStream.writeString(String str, boolean unshared)
private void writeString(String str, boolean unshared) throws IOException {
    handles.assign(unshared ? null : str); //放入HandleTable
    long utflen = bout.getUTFLength(str);
    if (utflen <= 0xFFFF) { //0xFFFF = 65535
        bout.writeByte(TC_STRING); //0x74
        bout.writeUTF(str, utflen); //以UTF格式寫入二進制流,先寫入長度,再寫入字符串內容
    } else {
        bout.writeByte(TC_LONGSTRING); //0x7C
        bout.writeLongUTF(str, utflen); //一樣以UTF格式先寫入長度再寫入內容
    }
}
  • 序列化數組對象: ObjectOutputStream.writeArray(Object obj, ObjectStreamClass desc, boolean unshared)
private void writeArray(Object array, ObjectStreamClass desc, boolean unshared) throws IOException {
    bout.writeByte(TC_ARRAY); //0x75
    writeClassDesc(desc, false); //寫入類描述符
    handles.assign(unshared ? null : array); //將array對象放入HandleTable

    Class<?> ccl = desc.forClass().getComponentType(); //獲取數組的類型
    //根據數組的實際類型執行不一樣的寫入操做
    if (ccl.isPrimitive()) { //原始數據類型及其對應的包裝類
        if (ccl == Integer.TYPE) {
            int[] ia = (int[]) array;
            bout.writeInt(ia.length);
            bout.writeInts(ia, 0, ia.length);
        } else if (ccl == Byte.TYPE) {
            byte[] ba = (byte[]) array;
            bout.writeInt(ba.length);
            bout.write(ba, 0, ba.length, true);
        } else if (ccl == Long.TYPE) {
            long[] ja = (long[]) array;
            bout.writeInt(ja.length);
            bout.writeLongs(ja, 0, ja.length);
        } else if (ccl == Float.TYPE) {
            float[] fa = (float[]) array;
            bout.writeInt(fa.length);
            bout.writeFloats(fa, 0, fa.length);
        } else if (ccl == Double.TYPE) {
            double[] da = (double[]) array;
            bout.writeInt(da.length);
            bout.writeDoubles(da, 0, da.length);
        } else if (ccl == Short.TYPE) {
            short[] sa = (short[]) array;
            bout.writeInt(sa.length);
            bout.writeShorts(sa, 0, sa.length);
        } else if (ccl == Character.TYPE) {
            char[] ca = (char[]) array;
            bout.writeInt(ca.length);
            bout.writeChars(ca, 0, ca.length);
        } else if (ccl == Boolean.TYPE) {
            boolean[] za = (boolean[]) array;
            bout.writeInt(za.length);
            bout.writeBooleans(za, 0, za.length);
        } else {
            throw new InternalError();
        }
    } else { //引用類型數組的處理:寫入數組長度,遍歷數組依次寫入每一個對象元素
        Object[] objs = (Object[]) array;
        int len = objs.length;
        bout.writeInt(len);
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "array (class \"" + array.getClass().getName() +
                "\", size: " + len  + ")");
        }
        try {
            for (int i = 0; i < len; i++) {
                if (extendedDebugInfo) {
                    debugInfoStack.push(
                        "element of array (index: " + i + ")");
                }
                try {
                    writeObject0(objs[i], false);
                } finally {
                    if (extendedDebugInfo) {
                        debugInfoStack.pop();
                    }
                }
            }
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }
}
  • 序列化枚舉類型對象: ObjectOutputStream.writeEnum(Enum<?> en, ObjectStreamClass desc, boolean unshared)
private void writeEnum(Enum<?> en, ObjectStreamClass desc, boolean unshared) throws IOException {
    bout.writeByte(TC_ENUM); //0x7E
    ObjectStreamClass sdesc = desc.getSuperDesc();
    writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
    handles.assign(unshared ? null : en);
    writeString(en.name(), false); //序列化枚舉對象的name屬性
}
  • 序列化對象:ObjectOutputStream.writeOrdinaryObject(Object obj,ObjectStreamClass desc, boolean unshared)
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException {
    if (extendedDebugInfo) {
        debugInfoStack.push(
            (depth == 1 ? "root " : "") + "object (class \"" +
            obj.getClass().getName() + "\", " + obj.toString() + ")");
    }
    try {
        //若是該類描述符表示的對象不容許序列化,將拋出InvalidClassException
        desc.checkSerialize();
        bout.writeByte(TC_OBJECT); //0x73
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        //若是該對象實現了java.io.Externalizable接口而且不是動態代理產生的對象,則調用writeExternalData方法
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else { //不然調用writeSerialData()序列化對象
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}
  • 序列化對象的實例數據,包括父類到子類: ObjectOutputStream.writeSerialData(Object obj, ObjectStreamClass desc)
private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException {
    //獲取表示被序列化對象的數據的佈局數組,父類在前
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;
        //若是被序列化對象複寫了writeObject方法則執行if塊的邏輯,不然執行defaultWriteFields方法
        if (slotDesc.hasWriteObjectMethod()) {
            PutFieldImpl oldPut = curPut;
            curPut = null;
            SerialCallbackContext oldContext = curContext;
            if (extendedDebugInfo) {
                debugInfoStack.push(
                    "custom writeObject data (class \"" +
                    slotDesc.getName() + "\")");
            }
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                slotDesc.invokeWriteObject(obj, this);
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } finally {
                curContext.setUsed();
                curContext = oldContext;
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }
            curPut = oldPut;
        } else {
            defaultWriteFields(obj, slotDesc);
        }
    }
}
  • 序列化默認的實例數據: ObjectOutputStream.defaultWriteFields(Object obj, ObjectStreamClass desc)
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {
    Class<?> cl = desc.forClass();
    if (cl != null && obj != null && !cl.isInstance(obj)) {
        throw new ClassCastException();
    }

    desc.checkDefaultSerialize();
    //準備一個字節數組primVals
    int primDataSize = desc.getPrimDataSize();
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    //獲取對象中的基本數據類型的數據並保存在primVals字節數組中
    desc.getPrimFieldValues(obj, primVals);
    //將基本數據類型的數據寫入底層字節容器中
    bout.write(primVals, 0, primDataSize, false);
    
    /** 獲取對應類的全部成員變量,ObjectStreamField類主要用來提取序列化過程當中某個對象內的字段的元數據
     * 信息,例如字段的類型、類型代碼、簽名等等
     */
    ObjectStreamField[] fields = desc.getFields(false);
    //準備一個對象數組用來存放引用類型的成員變量
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    desc.getObjFieldValues(obj, objVals);
    //遍歷對象數組,遞歸對引用類型的字段調用writeObject0()方法進行序列化
    for (int i = 0; i < objVals.length; i++) {
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "field (class \"" + desc.getName() + "\", name: \"" +
                fields[numPrimFields + i].getName() + "\", type: \"" +
                fields[numPrimFields + i].getType() + "\")");
        }
        try {
            writeObject0(objVals[i],
                         fields[numPrimFields + i].isUnshared());
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }
}

/**
 * 以以前的代碼示例爲例,執行writeObjcet方法會執行到該方法,所以:
 * 1. 獲取son對象的基本類型的字段數據,並寫入到底層的字節容器中
 * 2. 獲取son對象的引用類型字段數據,即parent對象,遞歸調用writeObjcet0()進行序列化
 * 3. 若是Person類有父類,會一併獲取父類的字段數據,寫入順序按照先父類後子類寫入。
 */

幾點說明: 1. 序列化開始時,首先寫入固定的magic number和版本號。對應temp.out開頭的AC ED 00 05 2. 根據待序列化對象或替代對象的類型分別執行不一樣的寫入方法,如null、原始類型、引用類型。 3. 寫入引用類型對象時,會寫入對象類型聲明標誌位、類描述聲明標記位、類描述信息(包括類名長度、序列號、serialVersionUID值、支持序列化標記、字段個數等等),而後從下到上遞歸序列化其父類的類描述信息。再以後是從父類中的成員屬性開始,從上到下遞歸序列化成員變量的信息。 4. 無論序列化哪些信息,都會在以前先寫入相應的標誌位,標識這是一個對象、一個描述符、數據長度亦或是數據類型等等。

temp.out文件解讀

參照以上源碼,能夠知道temp.out輸出文件的每1個或多個字節具體表明哪些信息:

  • temp.out文件內容
AC	ED	00	05	73	72	00	10	63	6F	6D	2E	6C	69	61	6E
67	2E	50	65	72	73	6F	6E	00	00	00	00	00	00	00	01
02	00	03	49	00	03	61	67	65	4C	00	04	6E	61	6D	65
74	00	12	4C	6A	61	76	61	2F	6C	61	6E	67	2F	53	74
72	69	6E	67	3B	4C	00	06	70	61	72	65	6E	74	74	00
12	4C	63	6F	6D	2F	6C	69	61	6E	67	2F	50	65	72	73
6F	6E	3B	78	70	00	00	00	0A	74	00	08	78	69	61	6F
6D	69	6E	67	73	71	00	7E	00	00	00	00	00	28	74	00
06	64	61	6D	69	6E	67	70

每兩個十六進制爲一個字節:

  • AC ED: 魔數。
  • 00 05: version值。
  • 73: TC_OBJECT,標記接下來寫入的是一個新Object對象。
  • 72:TC_CLASSDESC,標記接下來寫入的是類描述符信息。
  • 00 10:類名長度,com.liang.Person的長度16
  • 63 6F 6D 2E 6C 69 61 6E 67 2E 50 65 72 73 6F 6E:類名,字符串」com.liang.Person」的十六進制表示
  • 00 00 00 00 00 00 00 01:serialVersionUID,這裏是1L。
  • 02:可序列化標誌,當類實現了Serializable會寫入該標誌位
  • 00 03:序列化對象的成員屬性個數,這裏指name、age、parent三個成員變量。序列化成員屬性時順序是以變量名的字典序順序來進行,因此序列化順序是: age -> name -> parent。而且序列化成員變量會先寫入字段類型的字符編碼,字段類型與字符編碼的映射關係以下:
byte –> B, char->C, double->D, float->F, 
int->I,    long->J, short->S,  boolean->Z	
類或者接口->L, 數組->[
  • 49:age字段類型的字符編碼,age爲int型對應寫入編碼爲I,即16進制49。
  • 00 03:字段名的長度,age長度爲3。
  • 61 67 65:字段名,字符串」age」的十六進制表示。
  • 4C:name字段類型的字符編碼,name爲String型對應字符編碼爲L,轉爲16進製爲4C。
  • 00 04:name字段名的長度4.
  • 6E 61 6D 65:字符串」name」的十六進制表示
  • 74:TC_STRING,標記接下來寫入的是一個String。
  • 00 12:接下來寫入的String的長度爲十六進制的0012,換算成十進制就是18。
  • 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B:當成員屬性不是基本數據類型時,會寫入字段類型在JVM中的簽名。name是String類型,所以這裏寫入字符串’Ljava/lang/String;’的十六進制表示。
  • 49:parent字段類型的字符編碼,parent爲對象類型,寫入I的十六進制表示。
  • 00 06:字段名parent的長度6。
  • 70 61 72 65 6E 74:字段名」parent」的十六進制表示。
  • 74:TC_STRING,標記接下來寫入的是一個String。
  • 00 12:接下來寫入的字符串」 Lcom/liang/Person;」的長度爲18。
  • 4C 63 6F 6D 2F 6C 69 61 6E 67 2F 50 65 72 73 6F 6E 3B:寫入字段類型簽名」Lcom/liang/Person;」
  • 78:TC_ENDBLOCKDATA,標記對象數據塊的結束,到這裏son對象自己的數據塊就序列化完成了,接下來若是son對象所屬的類若是有父類,將會對父類的相關信息進行序列化,若是沒有父類也會寫入標記標識沒有父類。
  • 70:TC_NULL,這裏標識Person類沒有除Object之外的父類。

接下來將會爲從父類到子類的順序,爲每一個可序列化的對象寫入實例數據信息,即保存對象序列化時的狀態。

  • 00 00 00 0A:son對象中age屬性的值,換算成十進制即爲10。
  • 74:TC_STRING,接下來寫入的屬性值是一個String。
  • 00 08:該字符串的長度爲8。
  • 78 69 61 6F 6D 69 6E 67:son對象中name屬性的值,即’xiaoming’。
  • 73:TC_OBJECT,接下來寫入parent變量的值,parent是一個對象,所以須要寫入TC_OBJECT標記位。
  • 71:TC_REFERENCE,由於parent所屬的類com.liang.Person信息已經寫入過流了,不須要再一次序列化,因此這裏寫入TC_REFERENCE標誌。
  • 00 7E 00 00:一個固定的常量,表示第一個賦值的句柄。
  • 00 00 00 28: parent對象的成員屬性age的值40。
  • 74:TC_STRING,接下來寫入的屬性值是一個String。
  • 00 06:該字符串的長度爲6。
  • 64 61 6D 69 6E 67:parent對象中name屬性的值,即’daming’。
  • 70:TC_NULL,這裏標識parent對象的parent屬性值爲null。

反序列化簡單分析

反序列化入口函數爲ObjectInputStream.readObject(),該方法做用只是判斷應該調用readObjectOverride()仍是readObject0(boolean),而後在反序列化完成後,調用vlist成員變量的doCallbacks()方法執行回調邏輯。

下面主要分析readObject0方法:

private Object readObject0(boolean unshared) throws IOException {
    //檢查是否採用Data Block模式讀取
    boolean oldMode = bin.getBlockDataMode();
    if (oldMode) { //採用Data Block模式讀取
        //計算字節流中剩餘字節數,大於0或者沒有defaultDataEnd的值爲true則拋出OptionalDataException
        int remain = bin.currentBlockRemaining();
        if (remain > 0) {
            throw new OptionalDataException(remain);
        } else if (defaultDataEnd) {
            /*
             * Fix for 4360508: stream is currently at the end of a field
             * value block written via default serialization; since there
             * is no terminating TC_ENDBLOCKDATA tag, simulate
             * end-of-custom-data behavior explicitly.
             */
            throw new OptionalDataException(true);
        }
        bin.setBlockDataMode(false);
    }

    byte tc;
    //若是字節流中包含TC_RESET標記,調用handleReset()方法
    while ((tc = bin.peekByte()) == TC_RESET) {
        bin.readByte();
        handleReset();
    }

    depth++;
    totalObjectRefs++;
    try {
        /** 根據讀取的標記執行反序列化操做;
         *  這裏須要注意的一個點是當標記爲TC_ARRAY、TC_ENUM、TC_OBJECT、TC_STRING、TC_LONGSTRING時
         *  會對讀取的返回結果調用checkResolve方法,此時若是被反序列化的對象重寫了readResolve()方法
         *  那麼就會執行readResolve()方法,該方法能夠用來防止單例對象經過序列化反序列化的方式破壞單
         *  例模式。
         */
        switch (tc) {
            case TC_NULL:
                return readNull();

            case TC_REFERENCE:
                return readHandle(unshared);

            case TC_CLASS:
                return readClass(unshared);

            case TC_CLASSDESC:
            case TC_PROXYCLASSDESC:
                return readClassDesc(unshared);

            case TC_STRING:
            case TC_LONGSTRING:
                return checkResolve(readString(unshared));

            case TC_ARRAY:
                return checkResolve(readArray(unshared));

            case TC_ENUM:
                return checkResolve(readEnum(unshared));

            case TC_OBJECT:
                return checkResolve(readOrdinaryObject(unshared));

            case TC_EXCEPTION:
                IOException ex = readFatalException();
                throw new WriteAbortedException("writing aborted", ex);

            case TC_BLOCKDATA:
            case TC_BLOCKDATALONG:
                if (oldMode) {
                    bin.setBlockDataMode(true);
                    bin.peek();             // force header read
                    throw new OptionalDataException(
                        bin.currentBlockRemaining());
                } else {
                    throw new StreamCorruptedException(
                        "unexpected block data");
                }

            case TC_ENDBLOCKDATA:
                if (oldMode) {
                    throw new OptionalDataException(true);
                } else {
                    throw new StreamCorruptedException(
                        "unexpected end of block data");
                }

            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
    } finally {
        depth--;
        bin.setBlockDataMode(oldMode);
    }
}

從readObject0方法咱們沒有看到解析魔數和序列化版本號的方法,這是由於這一步操做是在實例化ObjectInputStream的構造函數中進行:

public ObjectInputStream(InputStream in) throws IOException {
    ......
    readStreamHeader();
    ......
}

protected void readStreamHeader() throws IOException, StreamCorruptedException {
    short s0 = bin.readShort();
    short s1 = bin.readShort();
    if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
        throw new StreamCorruptedException(
            String.format("invalid stream header: %04X%04X", s0, s1));
    }
}
相關文章
相關標籤/搜索