使用二進制流進行深拷貝不關流的原因

前段時間在書上學到了一個叫做原型模式的設計模式,學到了對象的淺拷貝和深拷貝。其中有一種針對深拷貝的實現方式是使用二進制流將對象裝入流中在從流中取出對象,這樣的對象是一個新的對象。常常信奉一句話就是,學到的總有一天會用到!就在前幾天我們項目中我對一個對象的list循環取它下面的另一個對象list,然後再循環內層對象的list,並對其中滿足條件的對象做修改再塞回外層對象中,結果外層List中的其他相同的對象也被改了值,後來才知道同事給相同的對象在MAP中放的是一份,所以看似改了一個,實際上改了多個。這時候,我的問題就出在了沒有用新的對象,所以想到了我之前在書上學到的對象深拷貝。好吧,好像扯遠了。

我們步入正題,直接上我在書上看的源碼:

public class People implements java.io.Serializable{
    
    
    public People deepClone() throws IOException,ClassNotFoundException{
        //將本對象放入流
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);
        //將對象從流中取出
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (People)ois.readObject();
    }
}
對於上面這段代碼,很多人第一眼就會覺得這個創建了流,並沒有關流,不規範!

我一開始也是這樣覺得,但後來我想人家寫到書上的東西不至於犯這種低級錯誤,於是我在網上搜索答案,發現使用二進制流來拷貝對象的案例中都沒有流的關閉操作,難道這些博主也會犯這種低級錯誤?不,我覺得不是,於是我開始查證原因。首先我寫了一個小程序跑案例,監控內存,發現關流和不關流的兩種代碼在內存中創建的對象數達到一定值之後就不在增加了,內存消耗也是一樣的。我們都知道,不關流會引起內存泄漏,可是這個代碼我不關流死循環的跑也沒問題。所以,我覺得我的猜想是對的,這個流不關是沒問題的。下面是我翻看源碼的記錄:

我們關流的順序是先關包裝流,再關被包裝流,先看包裝流(ObjectOutputStream)的close方法:

public void close() throws IOException {
    flush();
    clear();
    bout.close();
}
ObjectOutputStream的構造函數:

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;
    }
}
bout的來源是BlockDataOutputStream(ObjectOutputStream一個內部類),那麼再看BlockDataOutputStream的構造函數:

BlockDataOutputStream(OutputStream out) {
    this.out = out;
    dout = new DataOutputStream(this);
}
這裏的this.out又來自於我們傳進來的out,所以,我們調用oos的close()方法實際上最終是調用BlockDataOutputStream中的close(),如下代碼:

public void close() throws IOException {
    flush();
    out.close();
}
也就是說我們繞了一圈發現調用的close方法實際上是我們傳入的被包裝類的clos()方法,這真是個重大發現啊,那麼問題就簡單了,我們只需要找到被包裝類的close()方法就行了,下面看ByteArrayOutputStream的close方法:

public void close() throws IOException {
}
what?什麼都沒有,空實現!

那也就是說,我們要關的流最後都調的是這個空方法,那麼我們還需要關它嗎?顯然,我認爲是沒必要的。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章