上一篇講了類的序列化,今天要講類的反序列化,ObjectInputStream。
從內部變量中我們可以看出,內部包含一個塊輸入流,因爲有handle機制所以也有一個內部緩存表但不是hash表
/** 處理數據塊轉換的過濾流 */
private final BlockDataInputStream bin;
/** 確認調用返回列表 */
private final ValidationList vlist;
/** 遞歸深度 */
private long depth;
/** 對任何種類的對象、類、枚舉、代理的引用總數 */
private long totalObjectRefs;
/** 流是否關閉 */
private boolean closed;
/** 線柄->obj/exception映射 */
private final HandleTable handles;
/** 傳遞句柄值上下調用棧的草稿區 */
private int passHandle = NULL_HANDLE;
/** 當在字段值末尾因沒有TC_ENDBLOCKDATA阻塞時設置的標誌 */
private boolean defaultDataEnd = false;
/** b讀取原始字段值的緩衝區 */
private byte[] primVals;
/** 如果爲true,調用readObjectOverride()來替代readObject() */
private final boolean enableOverride;
/** if true, invoke resolveObject()如果爲true調用resolveObject() */
private boolean enableResolve;
/**
* 向上調用類定義的readObject方法期間的上下文,保持當前在被反序列化的對象和當前類的描述符。非readObject向上調用期間爲null。
*/
private SerialCallbackContext curContext;
/**
* 類描述符過濾器和從流中讀取的類,可能是null
*/
private ObjectInputFilter serialFilter;
從構造函數中可以看出,同樣需要驗證類的安全性,同時在初始化時會自動讀取Java序列化頭部信息。跟輸出流一樣,預留了一個生成全空的類輸入流初始化方法,用於繼承ObjectInputStream的子類進行重寫。安全驗證和輸出流中是相同的,而讀取頭部信息需要讀取魔數和版本號並驗證與當前JDK中的是否一致。當然,如果兩端使用的JDK中這個類版本號不一致就會出現異常。
/**
* 創建一個ObjectInputStream從指定的InputStream中讀取。一個序列化流頭部從這個流讀取和驗證。這個構造器會阻塞直到對應的ObjectOutputStream已經寫並刷新了頭部。
* 如果安裝了安全管理器,這個構造器會在被重寫了ObjectInputStream.readFields或ObjectInputStream.readUnshared的子類構造器
* 直接或間接調用的時候檢查enableSubclassImplementation序列化許可
*
* @param in input stream to read from
* @throws StreamCorruptedException 如果流頭部錯誤
* @throws IOException 在讀取流頭部是發生了IO錯誤
* @throws SecurityException 如果不被信任的子類非法重寫了安全敏感方法
* @throws NullPointerException 如果in是null
*/
public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
serialFilter = ObjectInputFilter.Config.getSerialFilter();
enableOverride = false;
readStreamHeader();
bin.setBlockDataMode(true);
}
protected ObjectInputStream() throws IOException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
bin = null;
handles = null;
vlist = null;
serialFilter = ObjectInputFilter.Config.getSerialFilter();
enableOverride = true;
}
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));
}
}
下面直接切入正題,來看一下readObject,讀和寫一樣也是final方法,所以子類要重寫必須通過readObjectOverride方法來完成。如果類裏面定義了非基本數據類型變量,需要進行嵌套讀取,outerHandle用來存儲上一層讀取對象的句柄,然後調用readObject0讀取生成對象。完成之後要記錄句柄依賴,並檢查有無異常產生。最上層讀取完成之後要發起回調。
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();//因爲是final方法,子類要重寫只能通過這裏
}
// 如果是嵌套讀取,passHandle包含封閉對象的句柄
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);//記錄句柄依賴
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();//最上層調用的讀取完成後,發起回調
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();//最上層調用退出時清除調用返回列表和句柄緩存
}
}
}
readObject0必須在塊輸入流內爲空也就是上一次反序列化完全結束後才能開始,否則會拋出異常。首先,從流中讀取一個標誌位,判斷當前下一個內容是什麼類型,上次我們講到過,輸出的時候都會先輸出TC_OBJECT,所以在默認情況下也是先讀取到TC_OBJECT,也就是先執行readOrdinaryObject
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {//之前是塊輸入模式且有未消費的數據會拋出異常
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* 流當前在字段值通過默認序列化塊寫的末尾,因爲沒有終止TC_ENDBLOCKDATA標記,模擬通常數據結尾的行爲
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {//讀到流reset標誌
bin.readByte();//消耗緩存的字節
handleReset();//depth爲0時執行clear方法,否則拋出異常
}
depth++;//增加遞歸深度
totalObjectRefs++;//增加引用對象數
try {
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);
}
}
readOrdinaryObject是讀取一個自定義類最上層直接調用的方法。幾個關鍵的點,首先是要讀取類的描述信息,然後根據類描述符創建一個實例,將對象實例存儲到句柄緩存中並將句柄存儲到passHandle,然後讀取內部變量數據,最後檢查有沒有替換對象
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);//讀取類描述信息
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
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);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);//將對象存儲到句柄緩存中
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
//讀取序列化內部變量數據
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);//標記句柄對應的對象讀取完成
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())//存在替換對象
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
那麼先來看下怎麼讀取類的描述信息readClassDesc,從上面方法的case中不能分析出,在沒有發生異常使用的情況下,此時的case只會進入TC_PROXYCLASSDESC或者TC_CLASSDESC也就是動態代理類的描述符合普通類的描述符。對於readProxyDesc讀取並返回一個動態代理類的類描述符,而readNonProxyDesc則是返回一個普通類的描述符,兩個方法都會設置passHandle到動態類描述符的設置句柄。如果類描述符不能被解析爲本地虛擬機中的一個類,一個ClassNotFoundException會被關聯到描述符的句柄中。關於動態代理和反射的部分下次要再仔細看一下。不過這裏又是讀取類描述,又是安全檢查還有類合法性檢查,跟直接在讀取端指定類相比額外多出很多開銷。
private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
ObjectStreamClass descriptor;
switch (tc) {
case TC_NULL:
descriptor = (ObjectStreamClass) readNull();
break;
case TC_REFERENCE:
descriptor = (ObjectStreamClass) readHandle(unshared);
break;
case TC_PROXYCLASSDESC:
descriptor = readProxyDesc(unshared);
break;
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
if (descriptor != null) {
validateDescriptor(descriptor);
}
return descriptor;
}
private ObjectStreamClass readProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_PROXYCLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);//添加描述符到句柄緩存中
passHandle = NULL_HANDLE;
int numIfaces = bin.readInt();//接口實現類數量
if (numIfaces > 65535) {
throw new InvalidObjectException("interface limit exceeded: "
+ numIfaces);
}
String[] ifaces = new String[numIfaces];
for (int i = 0; i < numIfaces; i++) {
ifaces[i] = bin.readUTF();//讀取接口名
}
Class<?> cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
try {
if ((cl = resolveProxyClass(ifaces)) == null) {
resolveEx = new ClassNotFoundException("null class");
} else if (!Proxy.isProxyClass(cl)) {
throw new InvalidClassException("Not a proxy");
} else {
//這個檢查等價於isCustomSubclass
ReflectUtil.checkProxyPackageAccess(
getClass().getClassLoader(),
cl.getInterfaces());
// Filter the interfaces過濾接口
for (Class<?> clazz : cl.getInterfaces()) {
filterCheck(clazz, -1);
}
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
// Call filterCheck on the class before reading anything else
filterCheck(cl, -1);
skipCustomData();
try {
totalObjectRefs++;
depth++;
desc.initProxy(cl, resolveEx, readClassDesc(false));//初始化類描述符
} finally {
depth--;
}
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);//將描述符存儲到句柄緩存中,返回句柄值
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
Class<?> cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
final boolean checksRequired = isCustomSubclass();
try {
if ((cl = resolveClass(readDesc)) == null) {//通過類描述解析類
resolveEx = new ClassNotFoundException("null class");
} else if (checksRequired) {
ReflectUtil.checkPackageAccess(cl);
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
// Call filterCheck on the class before reading anything else
filterCheck(cl, -1);
skipCustomData();
try {
totalObjectRefs++;
depth++;
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));//初始化類描述符
} finally {
depth--;
}
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
接下來是讀取序列化內部變量數據,由於readExternalData除了一些安全性的判斷外,直接調用了類中的方法,所以就不看了,直接看readSerialData中默認的讀取數據部分defaultReadFields。注意到基本數據類型可以直接通過字節輸入流來進行讀取,而非基本數據類型則需要遞歸調用readObject0來讀取。
private void defaultReadFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];//第一次進行需要初始化緩衝區,緩衝區不足時需要新建一個更大的緩衝區
}
bin.readFully(primVals, 0, primDataSize, false);//將字節流讀取到緩衝區
if (obj != null) {//從readSerialData進入這個方法時obj爲null
desc.setPrimFieldValues(obj, primVals);
}
int objHandle = passHandle;
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
ObjectStreamField f = fields[numPrimFields + i];
objVals[i] = readObject0(f.isUnshared());//遞歸讀取非基本數據類型類
if (f.getField() != null) {
handles.markDependency(objHandle, passHandle);//記錄依賴
}
}
if (obj != null) {
desc.setObjFieldValues(obj, objVals);
}
passHandle = objHandle;
}
然後看一下其他非基本數據類的讀取。readNull讀取null代碼並將句柄設置爲NULL_HANDLE,readString讀取UTF編碼,readArray需要先讀取數組長度,然後根據類的類型進行讀取。
private Object readNull() throws IOException {
if (bin.readByte() != TC_NULL) {
throw new InternalError();
}
passHandle = NULL_HANDLE;
return null;
}
private String readString(boolean unshared) throws IOException {
String str;
byte tc = bin.readByte();
switch (tc) {
case TC_STRING:
str = bin.readUTF();
break;
case TC_LONGSTRING:
str = bin.readLongUTF();
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
passHandle = handles.assign(unshared ? unsharedMarker : str);
handles.finish(passHandle);
return str;
}
private Object readArray(boolean unshared) throws IOException {
if (bin.readByte() != TC_ARRAY) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
int len = bin.readInt();
filterCheck(desc.forClass(), len);
Object array = null;
Class<?> cl, ccl = null;
if ((cl = desc.forClass()) != null) {
ccl = cl.getComponentType();
array = Array.newInstance(ccl, len);
}
int arrayHandle = handles.assign(unshared ? unsharedMarker : array);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(arrayHandle, resolveEx);
}
if (ccl == null) {
for (int i = 0; i < len; i++) {
readObject0(false);
}
} else if (ccl.isPrimitive()) {
if (ccl == Integer.TYPE) {
bin.readInts((int[]) array, 0, len);
} else if (ccl == Byte.TYPE) {
bin.readFully((byte[]) array, 0, len, true);
} else if (ccl == Long.TYPE) {
bin.readLongs((long[]) array, 0, len);
} else if (ccl == Float.TYPE) {
bin.readFloats((float[]) array, 0, len);
} else if (ccl == Double.TYPE) {
bin.readDoubles((double[]) array, 0, len);
} else if (ccl == Short.TYPE) {
bin.readShorts((short[]) array, 0, len);
} else if (ccl == Character.TYPE) {
bin.readChars((char[]) array, 0, len);
} else if (ccl == Boolean.TYPE) {
bin.readBooleans((boolean[]) array, 0, len);
} else {
throw new InternalError();
}
} else {
Object[] oa = (Object[]) array;
for (int i = 0; i < len; i++) {
oa[i] = readObject0(false);
handles.markDependency(arrayHandle, passHandle);
}
}
handles.finish(arrayHandle);
passHandle = arrayHandle;
return array;
}
readHandle方法從緩存中尋找該對象
private Object readHandle(boolean unshared) throws IOException {
if (bin.readByte() != TC_REFERENCE) {
throw new InternalError();
}
passHandle = bin.readInt() - baseWireHandle;//因爲寫的時候handle值加上了baseWireHandle
if (passHandle < 0 || passHandle >= handles.size()) {
throw new StreamCorruptedException(
String.format("invalid handle value: %08X", passHandle +
baseWireHandle));
}
if (unshared) {
// REMIND: what type of exception to throw here?
throw new InvalidObjectException(
"cannot read back reference as unshared");
}
Object obj = handles.lookupObject(passHandle);//緩存中尋找該對象
if (obj == unsharedMarker) {
// REMIND: what type of exception to throw here?
throw new InvalidObjectException(
"cannot read back reference to unshared object");
}
filterCheck(null, -1); // just a check for number of references, depth, no class檢查引用數量,遞歸深度,是否有這個類
return obj;
}
BlockDataInputStream
跟輸出時相同,對象輸入流也使用的是塊輸入流,輸入流有兩個模式:默認模式下,輸入數據寫入的格式和DataOutputStream相同;在塊數據模式,輸入的數據被塊數據標記歸爲一類。邏輯上來說,讀和寫應該是相對應的,主要是要判斷讀取到的頭部信息,然後根據不同的信息來讀取實際信息。有些跟輸出部分非常重複的就跳過了
內部變量主要需要注意兩個不同的輸入流,din是塊輸入流,in是取數輸入流,in中的下層輸入流是最初在構造ObjectInputStream中傳入的變量,所以通常是FileInputStream。
/** maximum data block length最大數據塊長度1K */
private static final int MAX_BLOCK_SIZE = 1024;
/** maximum data block header length最大數據塊頭部長度 */
private static final int MAX_HEADER_SIZE = 5;
/** (tunable) length of char buffer (for reading strings)可調節的字符緩衝的長度作爲供讀取的字符串 */
private static final int CHAR_BUF_SIZE = 256;
/** readBlockHeader() return value indicating header read may block readBlockHeader()返回的值指示頭部可能阻塞 */
private static final int HEADER_BLOCKED = -2;
/** buffer for reading general/block data讀取一般/塊數據的緩衝區 */
private final byte[] buf = new byte[MAX_BLOCK_SIZE];
/** buffer for reading block data headers讀取塊數據頭部的緩衝區 */
private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
/** char buffer for fast string reads快速字符串讀取的字符緩衝區 */
private final char[] cbuf = new char[CHAR_BUF_SIZE];
/** block data mode塊數據模式 */
private boolean blkmode = false;
// block data state fields; values meaningful only when blkmode true塊數據狀態字段,這些值僅在blkmode爲true時有意義
/** current offset into buf當前進入buf的偏移 */
private int pos = 0;
/** end offset of valid data in buf, or -1 if no more block data buf中有效值的結束偏移,沒有更多塊數據buf時爲-1 */
private int end = -1;
/** number of bytes in current block yet to be read from stream 在當前block中還沒有從流中讀取的字節數 */
private int unread = 0;
/** underlying stream (wrapped in peekable filter stream) 下層流包裝在可見過濾流中 */
private final PeekInputStream in;
/** loopback stream (for data reads that span data blocks) 迴路流用於擴展數據塊的數據讀取 */
private final DataInputStream din;
構造函數就是初始化兩個流變量
BlockDataInputStream(InputStream in) {
this.in = new PeekInputStream(in);
din = new DataInputStream(this);
}
setBlockDataMode將塊數據模式設爲給定的模式,true是打開,off是關閉,並返回之前的模式值。如果新的模式和舊模式一樣,什麼都不做。如果塊數據模式從打開被改爲關閉時有未消費的塊數據還留在流中,會拋出IllegalStateException
boolean setBlockDataMode(boolean newmode) throws IOException {
if (blkmode == newmode) {
return blkmode;
}
if (newmode) {//從關閉到打開,重置塊數據參數狀態
pos = 0;
end = 0;
unread = 0;
} else if (pos < end) {//從打開到關閉且有未消費的塊數據
throw new IllegalStateException("unread block data");
}
blkmode = newmode;
return !blkmode;
}
skipBlockData如果在塊數據模式,跳躍到數據塊的當前組的末尾但不會取消塊數據模式。如果不在塊數據模式,拋出IllegalStateException。這個方法調用了refill,refill用塊數據再裝滿內部緩衝區。任何buf中的數據在調用這個方法時被認爲是已經被消費了。設置pos、end、unread字段值來反映有效塊數據的數量。如果流中的下一個元素不是一個數據塊,設置pos=0和unread=0,end=-1
void skipBlockData() throws IOException {
if (!blkmode) {
throw new IllegalStateException("not in block data mode");
}
while (end >= 0) {
refill();
}
}
private void refill() throws IOException {
try {
do {
pos = 0;
if (unread > 0) {//當前block中還有未讀取的字節
int n =
in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE));//讀取unread和最大數據塊長度中的較小值的數據到buf中
if (n >= 0) {
end = n;//end爲讀取到的字節數
unread -= n;//當前block中還沒讀取的字節減少n
} else {
throw new StreamCorruptedException(
"unexpected EOF in middle of data block");
}
} else {
int n = readBlockHeader(true);
if (n >= 0) {
end = 0;
unread = n;
} else {
end = -1;
unread = 0;
}
}
} while (pos == end);//成功讀取到數據就會退出循環
} catch (IOException ex) {//出現異常說明下一個元素不是數據塊
pos = 0;
end = -1;
unread = 0;
throw ex;
}
}
readBlockHeader嘗試讀取下一個塊數據頭部。如果canBlock是false並且一個完整的頭部沒有阻塞時不能被讀取,返回HEADER_BLOCKED。否則如果流中下一個元素是一個塊數據頭部,返回頭部標識的這個塊數據的長度,否則返回-1
private int readBlockHeader(boolean canBlock) throws IOException {
if (defaultDataEnd) {
/*
* 流當前在一個字段值塊通過默認序列化寫入的末尾。因爲沒有終點TC_ENDBLOCKDATA標誌,
* 明確地模仿常規數據結束行爲。
*/
return -1;
}
try {
for (;;) {
int avail = canBlock ? Integer.MAX_VALUE : in.available();//可以阻塞時avail是最大整數,不能阻塞時是流中能讀取的字節數
if (avail == 0) {
return HEADER_BLOCKED;//沒有可讀取的字節數時返回HEADER_BLOCKED
}
int tc = in.peek();//從流中取數
switch (tc) {
case TC_BLOCKDATA://第二個字節是塊數據長度
if (avail < 2) {
return HEADER_BLOCKED;
}
in.readFully(hbuf, 0, 2);
return hbuf[1] & 0xFF;
case TC_BLOCKDATALONG://塊字節長度需要4個字節來表示
if (avail < 5) {
return HEADER_BLOCKED;
}
in.readFully(hbuf, 0, 5);
int len = Bits.getInt(hbuf, 1);//位運算獲取後4位的整數值
if (len < 0) {
throw new StreamCorruptedException(
"illegal block data header length: " +
len);
}
return len;
/*
* TC_RESET可能發生在數據塊之間。不幸的是,這個狀況必須在比其他類型標誌更低的級別被解析,
* 因爲原始數據讀取可能跨越被TC_RESET分割的數據塊
*/
case TC_RESET:
in.read();//跳過這個頭部
handleReset();//重置
break;
default:
if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) {
throw new StreamCorruptedException(
String.format("invalid type code: %02X",
tc));
}
return -1;
}
}
} catch (EOFException ex) {
throw new StreamCorruptedException(
"unexpected EOF while reading block data header");
}
}
單個字節的read和peek都是基於refill來完成的,而read多字節時,最大不能超過緩衝區內剩餘的字節,如果copy爲true,需要先將字節讀取到一個內部緩衝區再複製到目標數組,又增加開銷。
int read(byte[] b, int off, int len, boolean copy) throws IOException {
if (len == 0) {
return 0;
} else if (blkmode) {
if (pos == end) {
refill();
}
if (end < 0) {
return -1;
}
int nread = Math.min(len, end - pos);//最大不超過緩衝區內剩餘的字節
System.arraycopy(buf, pos, b, off, nread);
pos += nread;
return nread;
} else if (copy) {//copy模式先將數據讀取到一個緩衝區,再從緩衝區複製到目標數組
int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE));
if (nread > 0) {
System.arraycopy(buf, 0, b, off, nread);
}
return nread;
} else {
return in.read(b, off, len);
}
}
readFully循環調用read直到達到要讀取的字節數,如果不足會拋出EOFException
public void readFully(byte[] b, int off, int len, boolean copy)
throws IOException
{
while (len > 0) {
int n = read(b, off, len, copy);
if (n < 0) {
throw new EOFException();
}
off += n;
len -= n;
}
}
HandleTable
比起輸入流來,輸入流的緩存表多了一個HandleList[] deps用來存儲依賴,HandleList可以理解爲一個簡易版的ArrayList,當數組被填滿再次增加元素的時候,自動分配一個新的2倍大小的數組,把舊的元素複製過去再插入新的。另外兩個數組,Object[] entries存儲對象或異常,byte[] status存儲對象的狀態。並且這個緩存表並沒有使用hash,它是依靠ObjectInputStream中的passHandle來獲取位置的。
/** array mapping handle -> object status 句柄->對象狀態的數組映射*/
byte[] status;
/** array mapping handle -> object/exception (depending on status) */
Object[] entries;
/** array mapping handle -> list of dependent handles (if any) */
HandleList[] deps;
/** lowest unresolved dependency 最低的未解決依賴*/
int lowDep = -1;
/** number of handles in table */
int size = 0;
從assign我們可以看到,對象和它的狀態(默認爲UNKNOWN)被順序存儲到了數組中,並返回下標值
int assign(Object obj) {
if (size >= entries.length) {
grow();
}
status[size] = STATUS_UNKNOWN;
entries[size] = obj;
return size++;
}
關於deps這個數組到底是幹什麼的,我們可以一步步來分析。首先,尋找deps在HandleTable中被修改的地方,除了構造時初始化以外,finish中deps對應狀態爲UNKNOWN狀態的全部被設爲NULL,clear當中deps全部爲設爲null,grow當中新建了一個大小是原本2倍加1的deps數組並複製了原有內容來取代之前的數組,這些都不足以分析它的用途,關鍵在markDependency和markException兩個方法中。首先markException我們可以看到所有調用都是發生在出現ClassNotFoundException的時候,傳入的參數是這個類的handle值和異常。markDependency關聯一個ClassNotFoundException和當前活動句柄並傳播它到其他合適的引用對象。這個特定的句柄必須是打開的。簡單的來說,如果一個類解析失敗,那麼添加到entries緩存裏的對象會是一個異常,它對應的status爲STATUS_EXCEPTION
void markException(int handle, ClassNotFoundException ex) {
switch (status[handle]) {
case STATUS_UNKNOWN:
status[handle] = STATUS_EXCEPTION;//標記當前對象狀態爲異常
entries[handle] = ex;//修改緩存中的對象爲異常
// propagate exception to dependents傳播異常給依賴
HandleList dlist = deps[handle];
if (dlist != null) {
int ndeps = dlist.size();
for (int i = 0; i < ndeps; i++) {
markException(dlist.get(i), ex);//遞歸傳播依賴列表內的所有對象
}
deps[handle] = null;//清除依賴表中的對象,因爲只要再有對象關聯到相同類它必然是會從緩存中獲取異常,不再需要依賴表項
}
break;
case STATUS_EXCEPTION:
break;
default:
throw new InternalError();
}
}
再來看一下markDependency的調用,一處是在readObject調用readObject0之後,傳入參數是上一層對象的句柄和本層對象的句柄;一處是在readArray中讀取非基本數據類型調用readObject0之後,傳入參數也是上一層對象的句柄和本層對象的句柄;一處是在defaultReadFields中遞歸讀取非基本數據類中,傳入參數也是上一層對象的句柄和本層對象的句柄。總之,根據上面這幾個例子再結合方法的註釋,我們可以推測出這裏deps的作用是在一層層讀取對象時,比如自定義類中又又自定義類,記錄下一層指向上一層的關係,用來傳遞異常狀態。很明顯,從代碼中可以看出,在存在依賴雙方的情況下,如果上一層狀態是UNKNOWN,下一層狀態是EXCEPTION,則從依次向上將整個依賴表上所有的狀態全部置爲EXCEPTION,如果下一層也是UNKNOWN狀態,則在下一層的依賴表中增加上一層的句柄,並將未知狀態最底層設爲target。如果上一層狀態是OK,則出現InternalError,因爲上一層的句柄已經被關閉,不能再增加對它依賴的對象。
void markDependency(int dependent, int target) {
if (dependent == NULL_HANDLE || target == NULL_HANDLE) {
return;
}
switch (status[dependent]) {
case STATUS_UNKNOWN:
switch (status[target]) {
case STATUS_OK:
// ignore dependencies on objs with no exception忽略沒有異常的對象上依賴
break;
case STATUS_EXCEPTION:
// eagerly propagate exception急切傳播的異常
markException(dependent,
(ClassNotFoundException) entries[target]);//如果依賴是未知狀態也需將它們改爲異常狀態
break;
case STATUS_UNKNOWN:
// add to dependency list of target增加到依賴目標列表
if (deps[target] == null) {
deps[target] = new HandleList();
}
deps[target].add(dependent);
// remember lowest unresolved target seen記錄被看見最低的未解決目標
if (lowDep < 0 || lowDep > target) {
lowDep = target;
}
break;
default:
throw new InternalError();
}
break;
case STATUS_EXCEPTION:
break;
default:
throw new InternalError();
}
}
finish是關閉句柄,是唯一可以將狀態設爲STATUS_OK的方法,標記給出的句柄爲結束狀態,說明這個句柄不會有新的依賴標記。調用設置和結束方法必須以後進先出的順序。必須要handle之前不存在無法確認的狀態才能修改狀態爲OK
void finish(int handle) {
int end;
if (lowDep < 0) {
// 沒有還沒處理的未知狀態,只需要解決當前句柄
end = handle + 1;
} else if (lowDep >= handle) {
// handle之後存在還沒有處理的未知狀態,但上層句柄都已經解決了
end = size;
lowDep = -1;
} else {
// handle之前還有沒有處理的向前引用,還不能解決所有對象
return;
}
// change STATUS_UNKNOWN -> STATUS_OK in selected span of handles
for (int i = handle; i < end; i++) {
switch (status[i]) {
case STATUS_UNKNOWN:
status[i] = STATUS_OK;
deps[i] = null;
break;
case STATUS_OK:
case STATUS_EXCEPTION:
break;
default:
throw new InternalError();
}
}
}