OutputStreamWriter 不该包裹 BufferedOutputStream,因其自身已含字节缓冲,双重缓冲导致语义断裂、刷新不可控,违背IO分层原则;正确顺序是 BufferedWriter → OutputStreamWriter → FileOutputStream。

直接说结论:OutputStreamWriter 不该包裹 BufferedOutputStream,这种组合在逻辑上不成立,也违背 Java IO 的设计分层原则。
为什么不能把 OutputStreamWriter 套在 BufferedOutputStream 外面
OutputStreamWriter 是转换流,作用是把字符(char)按指定编码转成字节(byte),它本身已经封装了一层内部缓冲区,用于暂存编码后的字节。而 BufferedOutputStream 是字节缓冲流,只认 byte[],对字符、编码、Unicode 代理对等一无所知。
如果你写成这样:
Writer out = new OutputStreamWriter(new BufferedOutputStream(...));问题就来了:
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- OutputStreamWriter 写入字符时,先查编码表生成字节,再把字节塞进自己的缓冲区;
- 它调用底层 OutputStream 的 write(byte[]) 时,才把字节交给 BufferedOutputStream;
- 但 BufferedOutputStream 又会把这些字节再缓一次——等于同一份字节被缓了两层,且中间无协同机制;
- 更关键的是:OutputStreamWriter 的 flush() 只保证把它的字符编码缓冲刷到底层 OutputStream,但不触发 BufferedOutputStream 的刷盘;反之亦然。
正确的分层顺序:字节缓冲 → 转换 → 字符操作
真正高效又可控的链路是:
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"), "UTF-8"));这个结构里:
- FileOutputStream 是最底层的字节输出目标;
- OutputStreamWriter 在它之上完成字符→字节的编码(用 UTF-8),并自带小缓冲(默认 8192 字节);
- BufferedWriter 在最上层,提供字符串/行级写入、newLine()、大块字符缓冲能力;
- 调用 BufferedWriter.flush() 时,会逐层向下 flush:先刷 BufferedWriter 缓冲 → 再刷 OutputStreamWriter 缓冲 → 最终落到 FileOutputStream,真正写磁盘。
编码缓存与刷盘的实际联动细节
OutputStreamWriter 自身的缓冲区存储的是已编码的字节,不是原始字符。这意味着:
- 写入 "你好"(UTF-8 下占 6 字节)时,它先查表得 byte[]{(byte)0xE4, (byte)0xBD, ...},再存进自己的字节缓冲;
- 缓冲未满 + 未 flush + 未 close → 这些字节就卡在 OutputStreamWriter 内部,不会往下传;
- 只有 flush() 或 close() 被调用,它才批量调用底层 OutputStream.write(byte[]);
- 如果底层是 BufferedOutputStream,那这次 write(byte[]) 又被它接住,再次缓存——造成语义断裂和刷新不可控。
实际开发中该怎么做
按用途选组合:
- 写文本文件、需指定编码、要高效:用 BufferedWriter + OutputStreamWriter + FileOutputStream;
- 只写纯 ASCII 日志、不关心编码:可用 FileWriter(内部已含 OutputStreamWriter,默认平台编码)+ BufferedWriter;
- 需要手动控制字节级刷盘节奏(如网络协议头):直接用 BufferedOutputStream,自己 getBytes("UTF-8") 编码,再 write(byte[]);
- 绝不要出现 OutputStreamWriter(new BufferedOutputStream(...)) —— 编译虽过,运行易丢数据或乱码。

















