Java-IO流之缓冲流详解

    • 一、缓冲流概述
      • 1.1 什么是缓冲流
      • 1.2 缓冲流的工作原理
      • 1.3 缓冲流的优势
    • 二、字节缓冲流详解
      • 2.1 BufferedInputStream
        • 2.1.1 构造函数
        • 2.1.2 核心方法
        • 2.1.3 使用示例
      • 2.2 BufferedOutputStream
        • 2.2.1 构造函数
        • 2.2.2 核心方法
        • 2.2.3 使用示例
    • 三、字符缓冲流详解
      • 3.1 BufferedReader
        • 3.1.1 构造函数
        • 3.1.2 核心方法
        • 3.1.3 使用示例
      • 3.2 BufferedWriter
        • 3.2.1 构造函数
        • 3.2.2 核心方法
        • 3.2.3 使用示例
    • 四、缓冲流的性能优化
      • 4.1 缓冲区大小选择
      • 4.2 与其他流结合使用
      • 4.3 批量读写操作
    • 五、缓冲流的最佳实践
      • 5.1 使用try-with-resources语句
      • 5.2 合理选择缓冲区大小
      • 5.3 及时刷新缓冲区
    • 六、常见问题与解决方案
      • 6.1 缓冲区未刷新导致数据丢失
      • 6.2 缓冲流与mark/reset操作
      • 6.3 缓冲流性能问题

Java 中常见的输入输出(IO)操作,直接操作原始的字节流或字符流往往效率低下,尤其是处理大量数据时。Java IO体系中的缓冲流(Buffered Stream)通过引入缓冲区机制,显著提高了IO操作的性能。本文我将深入探讨Java缓冲流的原理、使用方法及性能优化技巧,帮你全面掌握这一重要技术。

一、缓冲流概述

1.1 什么是缓冲流

缓冲流是Java IO体系中用于装饰其他流的特殊流,它通过在内存中设置缓冲区,减少了直接与底层数据源(如磁盘、网络)的交互次数,从而提高了IO操作的效率。Java提供了四种缓冲流:

  • BufferedInputStream:字节输入缓冲流
  • BufferedOutputStream:字节输出缓冲流
  • BufferedReader:字符输入缓冲流
  • BufferedWriter:字符输出缓冲流

1.2 缓冲流的工作原理

缓冲流的核心是内部维护的一个缓冲区数组:

  • 输入缓冲流:从数据源读取数据时,先将数据批量读入缓冲区,后续的读取操作直接从缓冲区获取数据,减少了与数据源的交互次数。
  • 输出缓冲流:向目标写入数据时,先将数据写入缓冲区,当缓冲区满或调用flush()方法时,再将缓冲区中的数据批量写入目标,减少了与目标的交互次数。

1.3 缓冲流的优势

  • 提高IO性能:减少了与底层数据源的交互次数
  • 简化编程模型:提供了更方便的API,如BufferedReader的readLine()方法
  • 支持mark和reset操作:某些缓冲流支持标记和重置操作,增强了灵活性

二、字节缓冲流详解

2.1 BufferedInputStream

BufferedInputStream为字节输入流提供缓冲功能,继承自FilterInputStream

2.1.1 构造函数
  • BufferedInputStream(InputStream in):使用默认缓冲区大小(8192字节)创建缓冲流
  • BufferedInputStream(InputStream in, int size):使用指定大小的缓冲区创建缓冲流
2.1.2 核心方法
  • int read():从缓冲流读取一个字节
  • int read(byte[] b, int off, int len):从缓冲流读取字节到数组的指定位置
  • void close():关闭流并释放资源
  • void mark(int readlimit):标记当前位置
  • void reset():重置到最后标记的位置
2.1.3 使用示例
import java.io.*;public class BufferedInputStreamExample {public static void main(String[] args) {try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("large_file.dat"), 16384)) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = bis.read(buffer)) != -1) {// 处理读取的数据processData(buffer, bytesRead);}} catch (IOException e) {e.printStackTrace();}}private static void processData(byte[] buffer, int bytesRead) {// 数据处理逻辑}
}

2.2 BufferedOutputStream

BufferedOutputStream为字节输出流提供缓冲功能,继承自FilterOutputStream

2.2.1 构造函数
  • BufferedOutputStream(OutputStream out):使用默认缓冲区大小(8192字节)创建缓冲流
  • BufferedOutputStream(OutputStream out, int size):使用指定大小的缓冲区创建缓冲流
2.2.2 核心方法
  • void write(int b):向缓冲流写入一个字节
  • void write(byte[] b, int off, int len):向缓冲流写入字节数组的指定部分
  • void flush():刷新缓冲区,将数据写入底层输出流
  • void close():关闭流,关闭前会先刷新缓冲区
2.2.3 使用示例
import java.io.*;public class BufferedOutputStreamExample {public static void main(String[] args) {try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.dat"), 16384)) {byte[] data = generateData(1024 * 1024); // 生成1MB数据// 写入数据到缓冲区bos.write(data);// 确保所有数据都写入底层流bos.flush();} catch (IOException e) {e.printStackTrace();}}private static byte[] generateData(int size) {byte[] data = new byte[size];// 填充数据for (int i = 0; i < size; i++) {data[i] = (byte) (i % 256);}return data;}
}

三、字符缓冲流详解

3.1 BufferedReader

BufferedReader为字符输入流提供缓冲功能,并提供了读取整行的便捷方法。

3.1.1 构造函数
  • BufferedReader(Reader in):使用默认缓冲区大小(8192字符)创建缓冲流
  • BufferedReader(Reader in, int sz):使用指定大小的缓冲区创建缓冲流
3.1.2 核心方法
  • int read():读取单个字符
  • int read(char[] cbuf, int off, int len):读取字符到数组的指定位置
  • String readLine():读取一行文本,以换行符结束
  • void close():关闭流并释放资源
3.1.3 使用示例
import java.io.*;public class BufferedReaderExample {public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new FileReader("large_text_file.txt"), 32768)) {String line;while ((line = reader.readLine()) != null) {// 处理每行文本processLine(line);}} catch (IOException e) {e.printStackTrace();}}private static void processLine(String line) {// 处理文本行的逻辑}
}

3.2 BufferedWriter

BufferedWriter为字符输出流提供缓冲功能,并提供了写入换行符的便捷方法。

3.2.1 构造函数
  • BufferedWriter(Writer out):使用默认缓冲区大小(8192字符)创建缓冲流
  • BufferedWriter(Writer out, int sz):使用指定大小的缓冲区创建缓冲流
3.2.2 核心方法
  • void write(int c):写入单个字符
  • void write(char[] cbuf, int off, int len):写入字符数组的指定部分
  • void write(String s, int off, int len):写入字符串的指定部分
  • void newLine():写入一个行分隔符
  • void flush():刷新缓冲区
  • void close():关闭流,关闭前会先刷新缓冲区
3.2.3 使用示例
import java.io.*;public class BufferedWriterExample {public static void main(String[] args) {try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"), 32768)) {for (int i = 0; i < 100000; i++) {writer.write("这是第" + i + "行文本");writer.newLine(); // 写入换行符}// 确保所有数据都写入文件writer.flush();} catch (IOException e) {e.printStackTrace();}}
}

四、缓冲流的性能优化

4.1 缓冲区大小选择

缓冲区大小对性能有显著影响,一般来说:

  • 较大的缓冲区(如32KB或64KB)适合处理大文件或网络数据
  • 较小的缓冲区(如4KB或8KB)适合处理小文件或频繁的IO操作
  • 默认缓冲区大小(8KB)通常适用于大多数场景

性能测试示例

import java.io.*;public class BufferSizePerformanceTest {private static final int FILE_SIZE = 1024 * 1024 * 100; // 100MBprivate static final String TEST_FILE = "test_file.dat";public static void main(String[] args) {generateTestFile();int[] bufferSizes = {4096, 8192, 16384, 32768, 65536};for (int size : bufferSizes) {testReadPerformance(size);}}private static void generateTestFile() {try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_FILE))) {byte[] buffer = new byte[1024];for (int i = 0; i < FILE_SIZE / 1024; i++) {bos.write(buffer);}} catch (IOException e) {e.printStackTrace();}}private static void testReadPerformance(int bufferSize) {long startTime = System.currentTimeMillis();try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(TEST_FILE), bufferSize)) {byte[] buffer = new byte[1024];while (bis.read(buffer) != -1) {// 读取但不处理数据}} catch (IOException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("缓冲区大小: " + bufferSize + " 字节, 读取时间: " + (endTime - startTime) + " 毫秒");}
}

4.2 与其他流结合使用

缓冲流通常与其他流组合使用,形成功能强大的流管道:

示例:读取压缩文件中的文本

import java.io.*;
import java.util.zip.GZIPInputStream;public class CombinedStreamExample {public static void main(String[] args) {try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream("data.txt.gz"))))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}
}

4.3 批量读写操作

使用数组进行批量读写比单字节/字符读写效率更高:

高效写法

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.dat"))) {byte[] buffer = new byte[8192];int bytesRead;while ((bytesRead = bis.read(buffer)) != -1) {// 批量处理数据}
}

低效写法

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.dat"))) {int byteValue;while ((byteValue = bis.read()) != -1) {// 单字节处理数据}
}

五、缓冲流的最佳实践

5.1 使用try-with-resources语句

确保流资源被正确关闭,避免资源泄漏:

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"));BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {String line;while ((line = reader.readLine()) != null) {writer.write(processLine(line));writer.newLine();}
} catch (IOException e) {e.printStackTrace();
}

5.2 合理选择缓冲区大小

根据实际应用场景选择合适的缓冲区大小,避免过大或过小:

// 处理大文件时使用较大的缓冲区
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("large_file.dat"), 65536)) {// 处理数据
}

5.3 及时刷新缓冲区

在需要确保数据写入目标时,调用flush()方法:

try (BufferedWriter writer = new BufferedWriter(new FileWriter("log.txt"))) {// 写入重要日志writer.write("系统启动成功");writer.newLine();// 立即刷新,确保数据写入文件writer.flush();// 继续写入其他日志writer.write("开始处理数据");writer.newLine();
}

六、常见问题与解决方案

6.1 缓冲区未刷新导致数据丢失

如果在关闭流之前没有调用flush()方法,缓冲区中的数据可能不会被写入目标。使用try-with-resources语句可以避免这个问题,因为它会自动调用close()方法,而close()方法会先刷新缓冲区。

6.2 缓冲流与mark/reset操作

某些缓冲流支持mark和reset操作,但需要注意:

  • 标记位置不能超过缓冲区大小
  • 调用reset()后,只能读取到标记位置之后、缓冲区范围内的数据

示例

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("data.dat"))) {// 标记当前位置bis.mark(1024);// 读取一些数据byte[] buffer = new byte[512];bis.read(buffer);// 重置到标记位置bis.reset();// 可以再次读取之前的数据bis.read(buffer);
}

6.3 缓冲流性能问题

  • 如果发现使用缓冲流后性能反而下降,可能是因为:
    • 缓冲区设置过小,导致频繁刷新
    • 每次只读写少量数据,没有充分利用缓冲区
    • 底层IO设备本身速度很快,缓冲带来的收益有限

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/85867.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/85867.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/85867.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

健康检查:在 .NET 微服务模板中优雅配置 Health Checks

&#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks &#x1f4da; 目录 &#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks一、背景与意义 &#x1f50d;二、核心配置 &#x1f527;2.1 引入必要的 NuGet 依赖 &…

关于akka官方quickstart示例程序(scala)的记录

参考资料 https://doc.akka.io/libraries/akka-core/current/typed/actors.html#first-example 关于scala语法的注意事项 extends App是个语法糖&#xff0c;等同于直接在伴生对象中编写main 方法对象是通过apply方法创建的&#xff0c;也可以通过对象的名称单独创建&#x…

基于vue3-elemenyui的页面加载及新建浏览页案例

1.参考链接&#xff1a; 基于创建vue3链接&#xff1a;Vue3前端项目创建_vscode创建vue3项目-CSDN博客 基于创建elementui链接&#xff1a;Vue3引入ElementPlus_vue引入element-plus-CSDN博客 2.案例内容 该案例实现了基本的app.vue的路由跳转、新建浏览页参数传入以及浏览…

板凳-------Mysql cookbook学习 (十)

5.6 改变字符串的字符集或字符排序 mysql> set s1 my string; Query OK, 0 rows affected (0.01 sec)mysql> set s2 convert(s1 using utf8); Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> select charset(s1), charset(s2); -------------------------…

使用nginx配置反向代理,负载均衡

首先啥叫反向代理 咋配置呢&#xff0c;那当然是在nginx目录下改conf文件了 具体咋改呢&#xff0c;那就新增一个新的server配置&#xff0c;然后在location里新增你想代理的服务器 实际上负载均衡也就是根据反向代理的思路来的&#xff0c;如下所示 配置的话实际上也与上…

嵌入式开发之STM32学习笔记day20

STM32F103C8T6 PWR电源控制 1 PWR简介 PWR&#xff08;Power Control&#xff09;电源控制单元是STM32微控制器中一个重要的组成部分&#xff0c;它负责管理系统的电源管理功能&#xff0c;以优化功耗并提高效率。PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编…

Spring AI(10)——STUDIO传输的MCP服务端

Spring AI MCP&#xff08;模型上下文协议&#xff09;服务器Starters提供了在 Spring Boot 应用程序中设置 MCP 服务器的自动配置。它支持将 MCP 服务器功能与 Spring Boot 的自动配置系统无缝集成。 本文主要演示支持STDIO传输的MCP服务器 仅支持STDIO传输的MCP服务器 导入j…

Java八股文——集合「Set篇」

Set集合有什么特点&#xff1f;如何实现key无重复的&#xff1f; 面试官您好&#xff0c;Set 集合是 Java 集合框架中的一个重要接口&#xff0c;它继承自 Collection 接口&#xff0c;其最显著的特点和设计目标就是存储一组不重复的元素。 一、Set集合的主要特点&#xff1a…

「数据分析 - NumPy 函数与方法全集」【数据分析全栈攻略:爬虫+处理+可视化+报告】

- 第 104 篇 - Date: 2025 - 06 - 05 Author: 郑龙浩/仟墨 NumPy 函数与方法全集 文章目录 NumPy 函数与方法全集1. 数组创建与初始化基础创建序列生成特殊数组 2. 数组操作形状操作合并与分割 3. 数学运算基础运算统计运算 4. 随机数生成基础随机分布函数 5. 文件IO文件读写 …

报表/报告组件(二)-实例与实现解释

上篇《报表/报告组件(一)-指标/属性组件设计》介绍了组件核心指标/属性设计&#xff0c;本文以实例介绍各个特性的实现和效果&#xff0c;实例是多个报告融合&#xff0c;显示所有的特性。 设计 指标/属性组件是报告/报表关键部分&#xff0c;上篇已介绍过&#xff0c;本节回顾…

Flutter嵌入式开发实战 ——从树莓派到智能家居控制面板,打造工业级交互终端

一、为何选择Flutter开发嵌入式设备&#xff1f; 1. 跨平台能力降维打击 特性传统方案Flutter方案开发效率需分别开发Android/Linux一套代码多端部署内存占用200MB (QtWeb引擎)<80MB (Release模式)热重载支持不支持支持 2. 工业级硬件支持实测 树莓派4B&#xff1a;1080…

[蓝桥杯]机器人塔

题目描述 X 星球的机器人表演拉拉队有两种服装&#xff0c;A 和 B。 他们这次表演的是搭机器人塔。 类似&#xff1a; A B B A B A A A B B B B B A B A B A B B A 队内的组塔规则是&#xff1a; A 只能站在 AA 或 BB 的肩上。 B 只能站在 AB 或 BA 的肩上。 你的…

语雀文档保存失败URI malformed

原因 原因未知&#xff0c;我用deekseek将回答的答案复制到语雀文档时出现了这个异常&#xff0c;在知识库里面一直保存失败 语雀文档保存失败URI malformed 解决方案 使用小记&#xff0c;将里面的内容转移到小记里&#xff0c;将小记移到知识库中即可

小明的Java面试奇遇之互联网保险系统架构与性能优化

一、文章标题 小明的Java面试奇遇之互联网保险系统架构与性能优化&#x1f680; 二、文章标签 Java,Spring Boot,MyBatis,Redis,Kafka,JVM,多线程,互联网保险,系统架构,性能优化 三、文章概述 本文模拟了程序员小明在应聘互联网保险系统开发岗位时&#xff0c;参与的一场深…

从零开始的嵌入式学习day33

网络编程及相关概念 UDP网络通信程序 UDP网络通信操作 一、网络编程及相关概念 1. 网络编程概念&#xff1a; 指通过计算机网络实现程序间通信的技术&#xff0c;涉及协议、套接字、数据传输等核心概念。常见的应用场景包括客户端-服务器模型、分布式系统、实时通信等。…

Kotlin 1. 搭建Kotlin开发环境

本实战概述旨在指导用户搭建Kotlin开发环境&#xff0c;并进行简单的编程实践。首先&#xff0c;用户需安装IntelliJ IDEA&#xff0c;并进行基本设置&#xff0c;如选择主题、调整字体和安装插件等。接着&#xff0c;创建Kotlin项目&#xff0c;设置项目名称、位置和JDK版本&a…

Mysql的B-树和B+树的区别总结

B 树也称 B- 树&#xff0c;全称为 多路平衡查找树&#xff0c;B 树是 B 树的一种变体。B 树和 B 树中的 B 是 Balanced&#xff08;平衡&#xff09;的意思。 目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 BTree 作为索引结构。 B 树& B 树两者有何异同呢&…

COMSOL学习笔记-静电场仿真

最近在学习COMSOL&#xff0c;做了一个静电场仿真的例子&#xff0c;分享一下。 参考了下面的官方案例 计算电容 电容式位置传感器的边界元建模 三维模型 首先对静电测试仪进行三维建模。 Comsol静电场仿真 使用comsol进行静电场仿真&#xff0c;控制方程为泊松方程&#…

JavaScript 循环方法对比指南

JavaScript 循环方法对比指南 1. 标准 for 循环 语法&#xff1a; for (let i 0; i < arr.length; i) {console.log(arr[i]); }优点 ✅ 完全控制索引&#xff0c;适合需要精确控制遍历顺序或步长的场景。 ✅ 性能最优&#xff0c;在超大规模数据遍历时比高阶方法&#x…

【快餐点餐简易软件】佳易王快餐店点餐系统软件功能及操作教程

一、软件概述与核心优势 &#xff08;一&#xff09;试用版获取方式 资源下载路径&#xff1a;进入博主头像主页第一篇文章末尾&#xff0c;点击卡片按钮&#xff1b;或访问左上角博客主页&#xff0c;通过右侧按钮获取详细资料。 说明&#xff1a;下载文件为压缩包&#xff…