Java-IO流之缓冲流详解
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设备本身速度很快,缓冲带来的收益有限
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ
相关文章:
Java-IO流之缓冲流详解
Java-IO流之缓冲流详解 一、缓冲流概述1.1 什么是缓冲流1.2 缓冲流的工作原理1.3 缓冲流的优势 二、字节缓冲流详解2.1 BufferedInputStream2.1.1 构造函数2.1.2 核心方法2.1.3 使用示例 2.2 BufferedOutputStream2.2.1 构造函数2.2.2 核心方法2.2.3 使用示例 三、字符缓冲流详…...
vscode code runner 使用python虚拟环境
转载如下: zVS Code插件Code Runner使用python虚拟环境_coderunner python-CSDN博客...

Python实现markdown文件转word
1.markdown内容如下: 2.转换后的内容如下: 3.附上代码: import argparse import os from markdown import markdown from bs4 import BeautifulSoup from docx import Document from docx.shared import Inches from docx.enum.text import …...

NLP学习路线图(十七):主题模型(LDA)
在浩瀚的文本海洋中航行,人类大脑天然具备发现主题的能力——翻阅几份报纸,我们迅速辨别出"政治"、"体育"、"科技"等板块;浏览社交媒体,我们下意识区分出美食分享、旅行见闻或科技测评。但机器如何…...
深度学习之模型压缩三驾马车:基于ResNet18的模型剪枝实战(2)
前言 《深度学习之模型压缩三驾马车:基于ResNet18的模型剪枝实战(1)》里面我只是提到了对conv1层进行剪枝,只是为了验证这个剪枝的整个过程,但是后面也有提到:仅裁剪 conv1层的影响极大,原因如…...

综采工作面电控4X型铜头连接器 conm/4x100s
综采工作面作为现代化煤矿生产的核心区域,其设备运行的稳定性和安全性直接关系到整个矿井的生产效率。在综采工作面的电气控制系统中,电控连接器扮演着至关重要的角色,而4X型铜头连接器CONM/4X100S作为其中的关键部件,其性能优劣直…...

用ApiFox MCP一键生成接口文档,做接口测试
日常开发过程中,尤其是针对长期维护的老旧项目,许多开发者都会遇到一系列相同的困扰:由于项目早期缺乏严格的开发规范和接口管理策略,导致接口文档缺失,甚至连基本的接口说明都难以找到。此外,由于缺乏规范…...

在compose中的Canvas用kotlin显示多数据波形闪烁的问题
在compose中的Canvas显示多数据波形闪烁的问题:当在Canvas多组记录波形数组时,从第一组开始记录多次显示,如图,当再次回到第一次记录位置再显示时,波形出现闪烁。 原码如下: data class DcWaveForm(var b…...

【学习笔记】MIME
文章目录 1. 引言2. MIME 构成Content-Type(内容类型)Content-Transfer-Encoding(传输编码)Multipart(多部分) 3. 常见 MIME 类型 1. 引言 早期的电子邮件只能发送 ASCII 文本,无法直接传输二进…...
【深尚想】OPA855QDSGRQ1运算放大器IC德州仪器TI汽车级高速8GHz增益带宽的全面解析
1. 元器件定义与核心特性 OPA855QDSGRQ1 是德州仪器(TI)推出的一款 汽车级高速运算放大器,专为宽带跨阻放大(TIA)和电压放大应用优化。核心特性包括: 超高速性能:增益带宽积(GBWP&a…...

单北斗定位芯片AT9880B
AT9880B 是面向北斗卫星导航系统的单模接收机单芯片(SOC),内部集成射频前端、数字基带处理单元、北斗多频信号处理引擎及电源管理模块,支持北斗二号与三号系统的 B1I、B1C、B2I、B3I、B2a、B2b 频点信号接收。 主要特征 支持北斗二…...

旅游微信小程序制作指南
想创建旅游微信小程序吗?知道旅游业企业怎么打造自己的小程序吗?这里有零基础小白也能学会的教程,教你快速制作旅游类微信小程序! 旅游行业能不能开发微信小程序呢?答案是肯定的。微信小程序对旅游企业来说可是个宝&am…...

Ubuntu ifconfig 查不到ens33网卡
BUG:ifconfig查看网络配置信息: 终端输入以下命令: sudo service network-manager stop sudo rm /var/lib/NetworkManager/NetworkManager.state sudo service network-manager start - service network - manager stop :停止…...
zookeeper 学习
Zookeeper 简介 github:https://github.com/apache/zookeeper 官网:https://zookeeper.apache.org/ 什么是 Zookeeper Zookeeper 是一个开源的分布式协调服务,用于管理分布式应用程序的配置、命名服务、分布式同步和组服务。其核心是通过…...

【python深度学习】Day 45 Tensorboard使用介绍
知识点: tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战:MLP和CNN模型 效果展示如下,很适合拿去组会汇报撑页数: 作业:对resnet18在cifar10上采用微调策略下,用tensorboard监…...

【图像处理入门】5. 形态学处理:腐蚀、膨胀与图像的形状雕琢
摘要 形态学处理是基于图像形状特征的处理技术,在图像分析中扮演着关键角色。本文将深入讲解腐蚀、膨胀、开闭运算等形态学操作的原理,结合OpenCV代码展示其在去除噪声、提取边缘、分割图像等场景的应用,带你掌握通过结构元素雕琢图像形状的核心技巧。 一、形态学处理:基…...

并行智算MaaS云平台:打造你的专属AI助手,开启智能生活新纪元
目录 引言:AI助手,未来生活的必备伙伴 并行智算云:大模型API的卓越平台 实战指南:调用并行智算云API打造个人AI助手 3.1 准备工作 3.2 API调用示例 3.3 本地智能AI系统搭建 3.4 高级功能实现 并行智算云的优势 4.1 性能卓越…...
在 SpringBoot+Tomcat 环境中 线程安全问题的根本原因以及哪些变量会存在线程安全的问题。
文章目录 前言Tomcat SpringBoot单例加载结果分析多例加载:结果分析: 哪些变量存在线程安全的问题?线程不安全线程安全 总结 前言 本文带你去深入理解为什么在web环境中(Tomcat SpringBoot)会存在多线程的问题以及哪些变量会存在线程安全的…...

Day45 Python打卡训练营
知识点回顾: 1. tensorboard的发展历史和原理 2. tensorboard的常见操作 3. tensorboard在cifar上的实战:MLP和CNN模型 一、tensorboard的基本操作 1.1 发展历史 TensorBoard 是 TensorFlow 生态中的官方可视化工具(也可无缝集成 PyTorch&…...

2025年目前最新版本Android Studio自定义xml预览的屏幕分辨率
一、前言 在实际开发项目当中,我们的设备的分辨率可能会比较特殊,AS并没有自带这种屏幕分辨率的设备,但是我们又想一边编写XML界面,一边实时看到较为真实的预览效果,该怎么办呢?在早期的AS版本中ÿ…...

黑马Java面试笔记之 并发编程篇(线程池+使用场景)
一. 线程池的核心参数(线程池的执行原理) 线程池核心参数主要参考ThreadPoolExecutor这个类的7个参数的构造函数 corePoolSize 核心线程数目 maximumPoolSize 最大线程数目 (核心线程救急线程的最大数目) keepAliveTime 生存时间 - 救急线程的生存时…...
float和float32有什么区别
这是一个非常重要但容易混淆的问题。我们来一步一步讲清楚: ✅ 简答:float 和 float32 的区别 项目float(通用名称)float32(精确定义)含义通常指“浮点数”,具体精度由语言/平台决定明确指 32 …...

【AI学习】KV-cache和page attention
目录 带着问题学AI KV-cache KV-cache是什么? 之前每个token生成的K V矩阵给缓存起来有什么用? 为啥缓存K、V,没有缓存Q? KV-cache为啥在训练阶段不需要,只在推理阶段需要? KV cache的过程图解 阶段一:KV cac…...

七彩喜智慧养老平台:科技赋能下的市场蓝海,满足多样化养老服务需求
在人口老龄化加速与科技快速发展的双重驱动下,七彩喜智慧养老平台正成为破解养老服务供需矛盾、激活银发经济的核心引擎。 这一领域依托物联网、人工智能、大数据等技术,构建起覆盖居家、社区、机构的多层次服务体系。 既满足老年人多样化需求…...

《Pytorch深度学习实践》ch8-多分类
------B站《刘二大人》 1.Softmax Layer 在多分类问题中,输出的是每类的概率: 计算公式:保证了每类概率大于 0 ,又由保证了概率之和为 1; 举例如下: 2.Cross Entropy 计算损失: y np.array…...

国产录播一体机:科技赋能智慧教育信息化
在数字化时代,教育正经历着前所未有的变革。国产工控机作为信息化教育的核心载体,正在重新定义学习方式,赋能教师与学生,打造高效、互动、智能的教学环境,让我们一起感受科技与教育的深度融合!高能计算机推…...

关于逻辑回归的见解
逻辑回归通过将线性回归的输出映射到 [ 0 , 1 ] \left[0,1\right] [0,1]区间,来表示某个类别的概率。也就是其本质是先通过线性回归的预测值 y \boldsymbol{y} y输入到映射函数,既将线性回归的输出通过映射函数映射到 [ 0 , 1 ] \left[0,1\right] [0,1].常用的映射函数是sigm…...

Amazon Augmented AI:人类智慧与AI协作,破解机器学习审核难题
在人工智能日益渗透业务核心的今天,你是否遭遇过这样的困境:自动化AI处理海量数据时,面对模糊、复杂或高风险的场景频频“卡壳”?人工审核团队则被低效、重复的任务压得喘不过气?Amazon Augmented AI (A2I) 的诞生&…...
CMake入门:3、变量操作 set 和 list
在 CMake 中,set 和 list 是两个核心命令,用于变量管理和列表操作。理解它们的用法对于编写高效的 CMakeLists.txt 文件至关重要。下面详细介绍这两个命令的功能和常见用法: 一、set 命令:变量定义与赋值 set 命令用于创建、修改…...
聊聊FlaUI:让Windows UI自动化测试优雅起飞!
你还在为手动点点点测试Windows应用而感到膝盖疼?更愁于自动化测试工具价格贵得让钱包瑟瑟发抖?今天,我要给你安利一款“野路子有余,正经事儿也能干”的.NET UI自动化神器——FlaUI!别眨眼,看完你能少加三个…...