A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

昨天看老毕的基础视频看到BufferedWriter。对于BufferedWriter的close()方法,老毕讲得很清楚,关闭的是BufferedWriter所包装的FileWriter流,因此调用该close后,无需再调用FileWriter的close()方法。我比较喜欢追究细节,于是打开BufferedWriter的close()源码看了一下,是这样写的
  1. public void close() throws IOException {
  2.     synchronized (lock) {
  3.         if (out == null) {
  4.             return;
  5.         }
  6.         try {
  7.             flushBuffer();
  8.         } finally {
  9.             out.close();
  10.             out = null;
  11.             cb = null;
  12.         }
  13.     }
  14. }
复制代码


其中的out,就是指向你创建BufferedWriter对象时往构造函数里传的那个FileWriter。因此,BufferedWriter的close()方法确实关闭了底层的FileWriter流,无需再来一次close()。可是如果大家到网上一搜,就会发现还是有不少文章说BufferedWriter和它所包装的FileWriter的close()方法都要调用一次,而且必须先调BufferedWriter的close,再调FileWriter的close,否则会报异常,说流已关闭。我读到这里觉得疑惑,JDK API文档在BufferedWriter 和FileWriter 的close()方法中明明都写了一句:Closing a previously closed stream has no effect(关闭一个之前已被关闭的流,不会产生任何效果)啊,怎么先关BufferedWriter再关FileWriter就报异常了呢?

又把BufferedWriter的close()方法源码看了几遍,推测是那句flushBuffer()出了问题:假设做了如下声明:
  1. FileWriter fw = new FileWriter("demo.txt");
  2. BufferedWriter bw = new BufferedWriter(fw);
复制代码


如果调用fw.close()关闭流却没有把fw设为null,那么紧接着调用bw.close()时,if(out==null)判定就为假,将执行flushBuffer(),而这个flushBuffer()的源码是:
  1. public void flush() throws IOException {
  2.     synchronized (lock) {
  3.         flushBuffer();
  4.         out.flush();
  5.     }
  6. }
复制代码

它调用out(也就是fw)的flush()方法,然而此时流已经关闭,当然会报异常!

那么为何JDK API文档又说“Closing a previously closed stream has no effect”呢?呵呵,其实人家是说:如果你多次调用fw.close()【或】多次调用bw.close()不会出问题,而不是说你混合着调用两者不会出问题。对bw.close()来说,当你调用了一次后,它底层的Writer流就被设为null(out=null),下次再调用时被if(out==null)拦下,直接return,确实"has no effect"。另一方面,fw.close()的源码实际上是调用了一个叫StreamEncoding类的close()方法。这个类挺神秘,下载的JDK源码包里没它,得去http://www.docjar.com/html/api/sun/nio/cs/StreamEncoder.java.html 查看。它的close()方法代码如下:
  1. public void close() throws IOException {
  2.        synchronized (lock) {
  3.            if (!isOpen)
  4.                return;
  5.            implClose();
  6.            isOpen = false;
  7.        }
  8.    }
复制代码

哦,原来人家内部设置了一个boolean变量isOpen,关闭流的时候把这个变量设为false,下次再close()就直接return了。所以你fw.close()执行再多次也不会出问题。

总结一下:JDK API文档在BufferedWriter 和FileWriter 的close()方法中都写有一句:Closing a previously closed stream has no effect(关闭一个之前已被关闭的流,不会产生任何效果)。这句话只是告诉你,你可以多次调用同一个对象的close()方法,不会出问题。但是由于JDK源码实现的特点,如果你先调FileWriter的close再调BufferedWriter的close,就会报流关闭异常。必须先关闭BufferedWriter再关闭FileWriter。当然,从方便角度和逻辑层面来说,最佳的选择正是老毕基础视频里所做的那样,只调用BufferedWriter的close就可以了,省得给自己找麻烦。

评分

参与人数 1技术分 +1 收起 理由
格子、 + 1 很给力!

查看全部评分

14 个回复

倒序浏览
哇,喜欢你这种研究精神。。 强大
回复 使用道具 举报
戒风 来自手机 中级黑马 2014-7-18 12:16:12
藤椅
哇厉害,
回复 使用道具 举报
受教。。
回复 使用道具 举报
赵顺超 来自手机 中级黑马 2014-7-18 13:44:47
报纸
学习。。
回复 使用道具 举报
赞一个!
回复 使用道具 举报
研究精神赞一下!
回复 使用道具 举报
学习了~
回复 使用道具 举报
慕杰 初级黑马 2014-7-21 00:06:56
9#
  1. void flushBuffer() throws IOException {
  2.         synchronized (lock) {
  3.             ensureOpen();
  4.             if (nextChar == 0)
  5.                 return;
  6.             out.write(cb, 0, nextChar);
  7.             nextChar = 0;
  8.         }
  9.     }
复制代码


我想说我看到的flushBuffer()源码是这个样子的,ensureOpen的作用就是判断writer是否关闭,ensureOpen()的源码如下:
  1. /** Checks to make sure that the stream has not been closed */
  2.     private void ensureOpen() throws IOException {
  3.         if (out == null)
  4.             throw new IOException("Stream closed");
  5.     }
复制代码

所以这里throw了异常需要自己去处理吧?难道我JDK1.7的原因???我就是看了一下源码,没仔细看。
回复 使用道具 举报
感谢楼主分享
回复 使用道具 举报
假以时日,你将会变得很强大
回复 使用道具 举报
个人理解不一样,我看到那句话,就没有像你这样想着混合来关。

点评

:L 关闭2次不是我想到的,更不是我看到那句话想到的哈,那是网上流传的一种做法。。。  发表于 2014-7-22 09:43
回复 使用道具 举报
学习。。。
回复 使用道具 举报
版主都去哪了,明显这是加技术分的节奏
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马