不要以為字段以transient修飾的話就一定不會被序列化


 

 

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的子類實現自定義寫對象。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM