1: 先閱讀這邊文章:http://www.importnew.com/21517.html
2:被transient修飾真的會被序列化嗎?
反例:java.util.ArrayList中底層存儲數組就是transient,但是實際上還是可以被成功序列化。具體原因如下:
transient Object[] elementData;
我的測試代碼:
class ArrayListDemo implements Serializable { public static void main(String[] args) throws Exception { ArrayList<Integer> data = new ArrayList<>(); data.add(1); data.add(1); data.add(1); data.add(1); ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bo); out.writeObject(data); byte[] dataData = bo.toByteArray(); FileOutputStream fo = new FileOutputStream("data.dat"); fo.write(dataData); fo.close(); FileInputStream fi = new FileInputStream("data.dat"); ObjectInputStream in = new ObjectInputStream(fi); ArrayList<Integer> newList = (ArrayList<Integer>) in.readObject(); System.out.println(newList.size()); for (int i = 0; i < newList.size(); i++) { System.out.println(newList.get(i)); } } }
輸出:
數據還是成功序列化了,為什么會被序列化呢?分析原因還是需要看源碼,就以java.io.ObjectOutputStream#writeObject寫對象為入手點,跟蹤源碼會跟中到如下方法java.io.ObjectStreamClass#invokeWriteObject:
方法源碼如下:
void invokeWriteObject(Object obj, ObjectOutputStream out) throws IOException, UnsupportedOperationException { requireInitialized(); if (writeObjectMethod != null) { try { writeObjectMethod.invoke(obj, new Object[]{ out }); } catch (InvocationTargetException ex) { Throwable th = ex.getTargetException(); if (th instanceof IOException) { throw (IOException) th; } else { throwMiscException(th); } } catch (IllegalAccessException ex) { // should not occur, as access checks have been suppressed throw new InternalError(ex); } } else { throw new UnsupportedOperationException(); } }
通過debug可以看到最終調用的是java.util.ArrayList#writeObject ,java.util.ArrayList#writeObject源碼如下:
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }ke
可以看出最終將elementData寫出去了。反序列化同理不在分析。
重點來了,name為什么JDK需要這么做呢?原因如下:
JDK為什么不直接用elementData來序列化,而采用上訴的方式來實現序列化呢?原因在於elementData是一個緩存數組,它通常會預留一些容量,等容量不足時再擴充容量,那么有些空間可能就沒有實際存儲元素,采用上訴的方式來實現序列化時,就可以保證只序列化實際存儲的那些元素,而不是整個數組,從而節省空間和時間。
小提示:
java.io.ObjectOutputStream#writeObject中的enableOverride字段可以自行實現writeObjectOverride方法,對於enableOverride=true需要通過實現ObjectOutputStream的子類實現自定義寫對象。