本次講解中我們在上次的基礎上,深入的了解一下序列化的流程以及其中的原理。關於序列化的一些知識與使用,請參見我的另一篇博客:java基礎---->Serializable的使用。好了,我們進行以下分析的講解。
目錄導航
Java序列化的原理分析
java基礎---->Serializable的使用的代碼的演示,我們可以知道:
- 調用writeObject方法序列化一個對象,是將其寫入磁盤,以后在程序再次調用readObject時,根據wirteObject方法磁盤的文件重新恢復那個對象
- Externalizable 接口擴展了Serializable,並增添了兩個方法:writeExternal()和readExternal()。在序列化和重新裝配的過程中,會自動調用這兩個方法
Java的序列化
一、 objectOutputStream.writeObject(user)方法執行的詳細如下:
- ObjectOutputStream的構造函數設置enableOverride = false;
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; } }
- 所以writeObject方法執行的是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; } }
二、 在writeObject0方法中,以下貼出重要的代碼
if (obj instanceof 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) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } }
- 從上可以看出,如果對象沒有實現Serializable接口,在序列化的時候會拋出NotSerializableException異常
- 上述Person是一個類對象,所以會執行writeOrdinaryObject(obj, desc, unshared)方法;
三、 在writeOrdinaryObject(obj, desc, unshared)方法中,重要代碼如下:
if (desc.isExternalizable() && !desc.isProxy()) { writeExternalData((Externalizable) obj); } else { writeSerialData(obj, desc); }
- 如果對象實現了Externalizable接口,那么執行writeExternalData((Externalizable) obj)方法
- 如果對象實現的是Serializable接口,那么執行的是writeSerialData(obj, desc);
四, 我們首先看一下writeSerialData方法,主要執行方法:defaultWriteFields(obj, slotDesc);
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(); int primDataSize = desc.getPrimDataSize(); if (primVals == null || primVals.length < primDataSize) { primVals = new byte[primDataSize]; } desc.getPrimFieldValues(obj, primVals); bout.write(primVals, 0, primDataSize, false); ObjectStreamField[] fields = desc.getFields(false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; desc.getObjFieldValues(obj, objVals); 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(); } } } }
- 在此方法中,是系統默認的寫入對象的非transient部分。我們在后續的自定義序列化中會做講解。
五、 我們再來看一下writeExternalData的方法,重要代碼如下:
try { curContext = null; if (protocol == PROTOCOL_VERSION_1) { obj.writeExternal(this); } else { bout.setBlockDataMode(true); obj.writeExternal(this); bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); } }
- 在此方法中obj.writeExternal(this)執行了序列化對象的writeExternal方法,在上述例子中也就是User類的writeExternal方法。
Java的反序列化
一、 與上述的write過程類似,objectInputStream.readObject()方法執行了readObject0(false)方法:主要代碼:
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)); }
- 根據不同的對象類型做相應的處理,這里我們關注的是TC_OBJECT,執行的方法是:checkResolve(readOrdinaryObject(unshared));
七、 跟進去,我們可以看到在readOrdinaryObject(unshared)中,執行了以下代碼:
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) { handles.setObject(passHandle, obj = rep); } } return obj; }
- 加載序列化對象的Class,如果可以實例化,那么創建它的實例:desc.newInstance(),User類的無參構造方法執行
自定義Serializable的使用
一、 我們寫了Human類,為了方便Main方法也在這里類里面。
package com.huhx.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Human implements Serializable{ private static final long serialVersionUID = 1L; private String username; private transient String password; public Human(String username, String password) { this.username = "not transient " + username; this.password = "transient " + password; } @Override public String toString() { return username + "\n" + password; } private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException { inputStream.defaultReadObject(); password = (String) inputStream.readObject(); } private void writeObject(ObjectOutputStream outputStream) throws IOException { outputStream.defaultWriteObject(); outputStream.writeObject(password); } public static void main(String[] args) throws Exception { Human human = new Human("huhx", "liuli"); System.out.println("before: \n" + human); ByteArrayOutputStream buff = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(buff); objectOutputStream.writeObject(human); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray())); Human human2 = (Human) objectInputStream.readObject(); System.out.println("after: \n" + human2); } }
- 定義了兩種方法:readObject和writeObject方法,注意命名是有限制的,這個等下解釋。好了,我們先看結果
- 一個String 保持原始狀態,其他設為transient(臨時),以便證明非臨時字段會被defaultWriteObject()方法自動保存,而transient 字段必須在程序中明確保存和恢復。字段是在構建器內部初始化的,而不是在定義的時候,這證明了它們不會在重新裝配的時候被某些自動化機制初始化。若准備通過默認機制寫入對象的非transient 部分,那么必須調用defaultWriteObject(),令其作為writeObject()中的第一個操作;並調用defaultReadObject(),令其作為readObject()的第一個操作
二、 Human類中的readObject和writeObject方法的執行,不是我們調用的,而是ObjectInputStream與ObjectOutputStream 對象的readObject,writeObject方法去調用的。重要源代碼如下
- ObjectStreamClass(final Class<?> cl) 構造方法:
if (externalizable) { cons = getExternalizableConstructor(cl); } else { cons = getSerializableConstructor(cl); writeObjectMethod = getPrivateMethod(cl, "writeObject", new Class<?>[] { ObjectOutputStream.class }, Void.TYPE); readObjectMethod = getPrivateMethod(cl, "readObject", new Class<?>[] { ObjectInputStream.class }, Void.TYPE); readObjectNoDataMethod = getPrivateMethod( cl, "readObjectNoData", null, Void.TYPE); hasWriteObjectData = (writeObjectMethod != null); }
- 在writeSerialData的方法當中,有以下的代碼:利用反射機制去執行方法
slotDesc.invokeWriteObject(obj, this);
友情鏈接
- 本文測試代碼: 訪問密碼 12bb
- 序列化的一些知識與使用 java基礎---->Serializable的使用