前段时间在书上学到了一个叫做原型模式的设计模式,学到了对象的浅拷贝和深拷贝。其中有一种针对深拷贝的实现方式是使用二进制流将对象装入流中在从流中取出对象,这样的对象是一个新的对象。常常信奉一句话就是,学到的总有一天会用到!就在前几天我们项目中我对一个对象的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?什么都没有,空实现!
那也就是说,我们要关的流最后都调的是这个空方法,那么我们还需要关它吗?显然,我认为是没必要的。