我們都知道,序列化不會自動保存static和transient變量,因此我們若要保存它們,則需要通過writeObject()和readObject()去手動讀寫。
(01) 通過writeObject()方法,寫入要保存的變量。writeObject的原始定義是在ObjectOutputStream.java中,我們按照如下示例覆蓋即可:
private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject();// 使定制的writeObject()方法可以利用自動序列化中內置的邏輯。 out.writeInt(ival); // 若要保存“int類型的值”,則使用writeInt() out.writeObject(obj); // 若要保存“Object對象”,則使用writeObject() }
(02) 通過readObject()方法,讀取之前保存的變量。readObject的原始定義是在ObjectInputStream.java中,我們按照如下示例覆蓋即可:
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject(); // 使定制的readObject()方法可以利用自動序列化中內置的邏輯。 int ival = in.readInt(); // 若要讀取“int類型的值”,則使用readInt() Object obj = in.readObject(); // 若要讀取“Object對象”,則使用readObject() }
但是你會很驚奇盡管它們被外部類調用但事實上這是兩個private的方法。並且它們既不存在於java.lang.Object,也沒有在Serializable中聲明。那么ObjectOutputStream如何使用它們的呢?
作為程序員我們有一個終極大殺器,就是看源碼?
以ObjectInputStream 為例:
首先,會調用readObject(),通過Object obj = readObject0(false);調用readObject0;里面有這一句return checkResolve(readOrdinaryObject(unshared));調用
readOrdinaryObject方法 在readOrdinaryObject會調用readSerialData(obj, desc);然后readSerialData會調用slotDesc.invokeReadObject(obj, this);這里調用ObjectStreamClass的invokeReadObject(Object obj, ObjectInputStream in)
里面 readObjectMethod.invoke(obj, new Object[]{ in });這顯然是一個通過發射進行的方法調用,那么readObjectMethod是什么方法?別急繼續往下看,到ObjectStreamClass(final Class<?> cl)
發現里面有這一句readObjectMethod = getPrivateMethod(cl, "readObject",new Class<?>[] { ObjectInputStream.class },Void.TYPE),getPrivateMethod方法如下:
/**
* Returns non-static private method with given signature defined by given
* class, or null if none found. Access checks are disabled on the
* returned method (if any).
*/
private static Method getPrivateMethod(Class<?> cl, String name,
Class<?>[] argTypes,
Class<?> returnType)
{
try {
Method meth = cl.getDeclaredMethod(name, argTypes);
meth.setAccessible(true);
int mods = meth.getModifiers();
return ((meth.getReturnType() == returnType) &&
((mods & Modifier.STATIC) == 0) &&
((mods & Modifier.PRIVATE) != 0)) ? meth : null;
} catch (NoSuchMethodException ex) {
return null;
}
}
這下總算搞明白了。