Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

    • Java IO概述
    • IO基础

    • 常用IO流

      • 文件流
      • 缓冲流
        • 1. 为什么需要缓冲
        • 2. 字节缓冲流
        • 3. 字符缓冲流
        • 4. flush 的作用
        • 5. 缓冲区大小如何选择
        • 6. 常见误区
        • 专家实践清单
        • Tips 快问快答
        • 7. 总结
      • 数据流
      • 对象流与序列化
      • 打印流
      • 管道流
    • 文件与目录操作

    • Java NIO

    • 高级主题

    • 实践与性能优化

    • 面试与设计题

    • IO实践速查

  • JVM

目录

缓冲流

缓冲流用于减少程序和底层 IO 设备之间的直接交互次数,是 Java IO 中最常见的性能优化手段之一。

# 1. 为什么需要缓冲

磁盘和网络 IO 的成本远高于内存操作。如果程序每次只读写一个字节或一个字符,就可能触发大量系统调用。

缓冲流会在内存中维护一个缓冲区:

程序 <-> 内存缓冲区 <-> 文件或网络

读取时,一次从底层读取一批数据到缓冲区;程序再从缓冲区中取数据。写入时,先写入缓冲区,缓冲区满了或刷新时再写到底层。

# 2. 字节缓冲流

字节缓冲流包括:

  • BufferedInputStream
  • BufferedOutputStream
try (InputStream input = new BufferedInputStream(new FileInputStream("source.bin"));
     OutputStream output = new BufferedOutputStream(new FileOutputStream("target.bin"))) {

    byte[] buffer = new byte[8192];
    int len;
    while ((len = input.read(buffer)) != -1) {
        output.write(buffer, 0, len);
    }
}

即使外层已经有缓冲流,代码中仍然使用数组缓冲区是常见写法,可以减少方法调用次数,并让数据处理逻辑更明确。

# 3. 字符缓冲流

字符缓冲流包括:

  • BufferedReader
  • BufferedWriter

BufferedReader 最常用的能力是按行读取:

try (BufferedReader reader = Files.newBufferedReader(Path.of("app.log"), StandardCharsets.UTF_8)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

BufferedWriter 支持写入换行:

try (BufferedWriter writer = Files.newBufferedWriter(Path.of("result.txt"), StandardCharsets.UTF_8)) {
    writer.write("hello");
    writer.newLine();
    writer.write("world");
}

# 4. flush 的作用

写入缓冲流时,数据可能暂时停留在内存缓冲区中。flush() 用于强制把缓冲区内容写到底层目标。

writer.write("important message");
writer.flush();

需要显式 flush() 的场景:

  • 网络通信中希望对端立即收到数据。
  • 命令行交互中希望立即显示提示。
  • 长时间不关闭流但需要周期性写出。

文件写入中,如果马上关闭流,close() 会触发刷新。但重要数据仍然要考虑操作系统页缓存和磁盘刷盘,不是调用 flush() 就一定物理落盘。

# 5. 缓冲区大小如何选择

JDK 默认缓冲区通常已经适合大部分场景。自定义缓冲区时可以从 8KB、16KB、64KB 开始,不要盲目越大越好。

缓冲区过小:

  • 系统调用次数多。
  • 吞吐量低。

缓冲区过大:

  • 占用更多内存。
  • 对小文件没有明显收益。
  • 高并发场景下可能增加 GC 压力。

性能优化要结合文件大小、并发数、磁盘类型、网络情况和实际压测结果。

# 6. 常见误区

  • BufferedReader.readLine() 会去掉行尾换行符。
  • BufferedWriter.newLine() 写入的是系统相关换行符。
  • 使用缓冲输出流后,不关闭也不刷新可能导致数据没有真正写出。
  • 已经使用 Files.readAllLines 时,再加缓冲没有意义,因为数据已经整体读入内存。

# 专家实践清单

使用点 建议 原因
缓冲大小 从 8KB、16KB、64KB 试起 结合文件大小和吞吐压测
输出刷新 关闭前刷新或直接关闭 防止数据停留在内存缓冲区
按行读取 使用 BufferedReader 避免一次性加载文本
高频小写入 使用 BufferedWriter 减少系统调用

缓冲流的数据路径:

业务代码
  │ 小块读写
  ▼
内存缓冲区
  │ 批量读写
  ▼
底层文件/网络流

# Tips 快问快答

Q:缓冲流解决什么性能问题? A:减少频繁底层 IO 调用,把多次小读写合并成较少的大读写。

Q:缓冲区越大越好吗? A:不是。过大浪费内存,过小系统调用频繁,需要压测选择。

Q:flush 的作用是什么? A:把缓冲区中尚未写出的数据推到底层输出流。

Q:关闭缓冲输出流会自动 flush 吗? A:通常会,关闭前会刷新缓冲并关闭底层流。

Q:BufferedReader.readLine 适合什么场景? A:适合按行处理日志、配置、CSV 等文本。

Q:已经使用 Files.readAllBytes 还需要缓冲吗? A:不需要,数据已经一次性读入内存。

Q:缓冲流会改变数据内容吗? A:不会,它只改变读写批量和性能特征。

Q:最常见的缓冲流错误是什么? A:写完不关闭不刷新,导致数据没有真正写出。

# 7. 总结

缓冲流是 Java IO 中最常用的增强流。它通过减少底层 IO 次数提升性能,特别适合频繁小块读写。生产代码中,文件流、网络流通常都会配合缓冲流使用。

上次更新: 2026/06/25, 14:19:18
文件流
数据流

← 文件流 数据流→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式