Java:Serializable反序列化過程

在 篇文章中,我們大致瞭解了Serializable的寫入過程,我們用到的是

ObjectOutputStream,那與之對應的就是反序列化,把存入文件的二進制數據,讀出來,轉換為對應的實例對象,這次就該用到ObjectInputStream了。使用代碼大致如下:

<code>TesyBean sBean = new TesyBean();
String path = "/Users/menghui/Downloads/mh.obj";
FileInputStream fileInputStream;
File file = new File(path);
try {
fileInputStream = new FileInputStream(file.toString());
try {
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//這裡讀出數據,轉換為具體實例
sBean = (TesyBean) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}

} catch (FileNotFoundException e) {
e.printStackTrace();
}/<code>

使用過程中,先通過FileInputStream將文件數據讀入,然後創建ObjectInputStream,並傳入FileInputStream數據流,通過readObject將數據讀出,並轉換為具體實例對象。來看代碼。

<code>public final Object readObject()
throws IOException, ClassNotFoundException
{

......
try {
Object obj = readObject0(false);
......
return obj;
} finally {
......
}
}/<code>

先檢查是否有override,和寫入過程是一樣的,然後執行readObject0方法。

<code>private Object readObject0(boolean unshared) throws IOException {
......

//tc代表一個標識,表明存儲的是什麼類型數據
byte tc;
//先peek一個字節,判斷是否是reset標識,如果是就重置數據流,直到檢索到非reset標記為止
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}

depth++;
totalObjectRefs++;
try {
//判斷讀到的tc值
switch (tc) {
......

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

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

......

default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
......
}
}/<code>

tc這個標記,對於我們這個例子來說,就是TC_OBJECT,這個值是在ObjectOutputStreamwriteOrdinaryObject方法中寫入的。對於TC_OBJECT標記,將繼續執行readOrdinaryObject方法。readOrdinaryObject中會通過readClassDesc先把類型描述讀取出來,這些描述數據是在ObjectOutputStreamwriteOrdinaryObject中調用writeClassDesc寫入的,包括標識TC_CLASSDESC。來看readClassDesc。

<code>private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
ObjectStreamClass descriptor;

switch (tc) {
......
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);
break;
......
}
......
}/<code>

繼續執行readNonProxyDesc(非動態代理的)。內部執行readClassDescriptor

<code>protected ObjectStreamClass readClassDescriptor()
throws IOException, ClassNotFoundException
{
ObjectStreamClass desc = new ObjectStreamClass();
desc.readNonProxy(this);
return desc;
}/<code>

readClassDescriptor中會讀取每個字段的信息,這些都是在 ObjectStreamClasswriteNonProxy寫入的。

<code>void readNonProxy(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
......
//讀出字段數量
int numFields = in.readShort();
......
//創建一個數組,用來保存每個字段信息
fields = (numFields > 0) ?
new ObjectStreamField[numFields] : NO_FIELDS;
//循環讀出每個字段信息

for (int i = 0; i < numFields; i++) {
char tcode = (char) in.readByte();
String fname = in.readUTF();
String signature = ((tcode == 'L') || (tcode == '[')) ?
in.readTypeString() : new String(new char[] { tcode });
try {
//創建對應的ObjectStreamField對象
fields[i] = new ObjectStreamField(fname, signature, false);
} catch (RuntimeException e) {
throw (IOException) new InvalidClassException(name,
"invalid descriptor for field " + fname).initCause(e);
}
}
//和寫入時一樣,計算空間
computeFieldOffsets();
}/<code>

上面的過程完成後,我們回到readNonProxyDesc。

<code>......
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
......


//這裡面會初始化一些內部變量,並檢查對象的uid是否一致,如果不一致就會報錯,uid的作用就在於此
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));/<code>

readNonProxyDesc獲取到正確的描述之後,回到ObjectInputStreamreadOrdinaryObject

<code>......
ObjectStreamClass desc = readClassDesc(false);
......

Object obj;
try {
//判斷對象是否可以創建實例
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}

......
//最後通過這裡讀取數據
readSerialData(obj, desc);/<code>

isInstantiable判斷對象描述信息中是否可以創建實例,可以的話就通過反射執行構造函數,否則就置為null

<code>    boolean isInstantiable() {
requireInitialized();
return (cons != null);
}

/** ObjectStreamClass中有一個代表構造方法的變量,也是通過反射得到對象的構造函數 */
private Constructor> cons;/<code>

一切OK後,就是readSerialData—>defaultReadFields讀取數據的過程了。裡面依然是通過獲取字段及其值的數組,然後賦值的,和寫入過程一樣。


分享到:


相關文章: