最近在写一个功能,用到了BufferedWriter这个类,因为一般在new这个类的时候很少说去显式指定缓冲区大小,默认都是8K的字符数组。然后我就想了些关于缓冲流里面缓冲区的细节问题,这个方法大家都知道,就是假如缓冲区没有满,需要手动执行flush或close来清空缓冲区并把数据写到文件中;如果缓冲区自己满了,就会自动写入到文件。于是我想测试一下第二句话的效果
如果缓冲区自己满了,就会自动写入到文件
然后我把代码写了个demo如下:
public static void Test1() throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("abc3\\noFlush.txt"), 2);
bw.write(new char[]{'我','爱','你','祖','国'});
}
显式指定缓冲区大小为2,按照目前的理解就是,write的数据大小是5,超出了缓冲区大小,那就在第一轮就会先在缓冲区存入“我、爱”这两个字符,缓冲区满了自动写入文件;第二轮存入“你、祖”,缓冲区满了又自动写入文件;最后一个“国”字因为存在于缓冲区但还没有满,所以没有写入到文件。
但是!实际上noFlush.txt文件里面竟是空白一片!!
我就纳闷了,不是write的数据大小超过指定的缓冲区大小就会自动写入文件嘛?为啥我这个文件里面啥都没有。通俗来讲,解决办法就是在代码结尾使用flush或者close方法,这样就可以写入到文件了,但这并不是我要的效果。
我目的是想证明“如果缓冲区自己满了,就会自动写入到文件”。
后面查阅了很多关于缓冲区的细节资料,好像都并没有我想要的那个答案,绝大部分对于缓冲区的解释都偏向于满了会自动写入等等。
于是我咨询了一些大佬,大佬们给了我一个比较好的理解思路,说咱们在new BufferedWriter时指定的缓冲区大小只是给它自带的缓冲区设置的。其实还有一个缓冲区,就是在StreamEncoder这个类里面的缓冲区,这个类也自带一个缓冲区,并且大小默认固定为8K。两个缓冲区还是有区别的,毕竟是各自的缓冲区。
那么咱们的BufferedWriter和StreamEncoder又有什么关系呢?然后我搜到了一些相关的文章也找到了答案,文章地址在这,这篇文章提到了BufferedWriter在进行write时的一些过程。
按照我们目前的需求去理解write的细节:
我们现在write的数据长度不是5嘛?很明显超出了BufferedWriter内部缓冲区为2的大小,
于是在BufferedWriter.write(cbuf, off, len) 里,有一段比较重要的方法逻辑:
如果数据长度超过缓冲区的大小,就会执行如下代码:
flushBuffer(); //这个flushBuffer方法里面会先判断:如果nextChar为0就return //nextChar是字符缓冲区中的已存储元素的位置,初始化默认为0 //程序走到该方法时,nextChar也没改动过,所以这个方法返回return,相当于没有执行 //然后着重看下面这个方法 out.write(cbuf, off, len);
这个out对象就是你在new BufferedWriter时传入的Writer的子类实现,如传入的是FileWriter类。
然后这个FileWriter类又是继承了OutputStreamWriter类,但因为这个FileWriter类没有自己的write方法,所以用的是父类OutputStreamWriter的write(cbuf, off, len)方法,而OutputStreamWriter的write方法又去执行StreamEncoder的write方法,所以最后的落点是在StreamEncoder类这里。相关信息可以看下面图:
因此在前面代码demo里,即使我们设置了BufferedWriter内部缓冲区的大小为2,到这也用不上了,因为人家程序直接是走了out.write(cbuf,off,len)这个方法,完全没用到Buffer的缓冲区。
然后前面提到又因为StreamEncoder类里面自带8K的缓冲区,所以前面经过层层传递过来的“我爱你祖国”五个字符数据到了StreamEncoder类这儿,最终逻辑其实也就只是把这五个字符长度的数据放到8K的缓冲区里面
所以实际上对于StreamEncoder内部缓冲区来讲并没有塞满,这样一想,那既然没放满,我们也没有手动去flush和close,又怎能写入文件呢?