Java里面BufferedWriter在超过设置的缓冲区大小时数据不会flush写入文件的问题

最近在写一个功能,用到了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方法,所以用的是父类OutputStreamWriterwrite(cbuf, off, len)方法,而OutputStreamWriterwrite方法又去执行StreamEncoderwrite方法,所以最后的落点是在StreamEncoder类这里。相关信息可以看下面图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

因此在前面代码demo里,即使我们设置了BufferedWriter内部缓冲区的大小为2,到这也用不上了,因为人家程序直接是走了out.write(cbuf,off,len)这个方法,完全没用到Buffer的缓冲区。

然后前面提到又因为StreamEncoder类里面自带8K的缓冲区,所以前面经过层层传递过来的“我爱你祖国”五个字符数据到了StreamEncoder类这儿,最终逻辑其实也就只是把这五个字符长度的数据放到8K的缓冲区里面

所以实际上对于StreamEncoder内部缓冲区来讲并没有塞满,这样一想,那既然没放满,我们也没有手动去flush和close,又怎能写入文件呢?

### 回答1: 在Java的IO操作中,当使用缓冲输出流(BufferedOutputStream)或者打印流(PrintStream)输出数据时,数据通常会先被缓冲区,直到缓冲区满了或者手动调用flush()方法后才会将数据刷出到目标流(比如文件、网络连接、标准输出控制台等)。 具体来说,以下情况会导致数据被缓存而不直接输出到目标流: - 使用BufferedOutputStream进行输出时,数据会被先入到缓冲区,直到缓冲区满了或者调用flush()方法才会将数据刷出到目标流。 - 使用PrintStream进行输出时,默认情况下也是使用缓冲输出的方式。只有在调用了println()方法或者flush()方法时,才会将缓冲区中的数据输出到目标流。 - 对于System.out输出流,它是一个PrintStream类型的对象,默认也使用缓冲输出方式。只有当程序执行结束时,或者手动调用System.out.flush()方法时,才会将缓冲区中的数据输出到控制台。 需要注意的是,由于缓冲输出的机制,如果在输出完数据后没有及时调用flush()方法或者关闭流,可能会导致数据丢失或者输出不完整的情况发生。因此,在使用缓冲输出流时,建议及时调用flush()方法或者关闭流,以保证数据的完整性和正确性。 ### 回答2: 在 Java 的 IO 操作中,当使用输出流向控制台或文件数据时,通常会使用缓冲区来提高入性能。数据会先被缓冲区,然后在特定的条件下才会真正被输出到目标设备(如控制台或文件)。以下情况下数据会输出到缓冲区,而不会立即输出到控制台,需要调用 flush() 函数才能输出: 1. 缓冲区满:当缓冲区达到一定的容量或者被满时,数据会被自动输出到目标设备之前的缓冲区会被清空。 2. 手动调用 flush():在需要立即输出缓冲区数据时,可以显式地调用 flush() 函数。 3. 关闭输出流:当关闭输出流时,会自动将缓冲区中的数据输出。 需要注意的是,在调用 flush() 函数后,并不意味着数据立即输出到目标设备,而是将缓冲区中的数据刷新到目标设备之前。同时,需要及时关闭输出流,以确保所有数据被成功输出并资源正确释放。 总结来说,在 Java 的 IO 操作中,数据会首先被缓冲区,确保高效的输出操作。只有在特定的条件下(如缓冲区满、手动调用 flush()、关闭输出流)数据才会被真正输出到目标设备,增加程序的灵活性和性能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值