当前位置: 首页 > news >正文

22_Java缓冲流与转换流

Java缓冲流与转换流

文章目录

  • Java缓冲流与转换流
    • 前言
    • 一、缓冲流的原理
    • 二、字节缓冲流
      • 2.1 BufferedInputStream
      • 2.2 BufferedOutputStream
    • 三、字符缓冲流
      • 3.1 BufferedReader
      • 3.2 BufferedWriter
    • 四、转换流:InputStreamReader
    • 五、转换流:OutputStreamWriter
    • 六、流的装饰组合
    • 七、性能对比实验
    • 总结
    • ✅ 亮点总结
    • 适用场景
    • 扩展方向

前言

上一篇我们学习了Java IO流的体系框架和基础用法。在实际开发中,直接使用FileInputStreamFileReader进行逐字节或逐字符读写效率较低,因为每次read操作都会触发一次磁盘IO。Java提供了缓冲流转换流来解决性能和编码问题。缓冲流通过内置缓存区减少实际IO次数,转换流则在字节流和字符流之间架起桥梁。

理解这两类流是掌握Java IO"装饰器模式"的关键一步。如果把基础流(FileInputStream等)看作"水管",缓冲流就是"水塔"——先把水存储起来,再批量传输;转换流则是"翻译官"——帮你在字节和字符之间进行编码转换。两者结合使用,构成了Java IO编程中最常见的实践模式。本文将深入讲解这两类流的设计原理、使用方法和性能对比。

一、缓冲流的原理

普通IO流每次读/写操作都需要与底层设备交互,产生大量系统调用,性能较低。缓冲流内部维护了一个缓冲区数组。为了理解缓冲流的重要性,可以做一个类比:如果没有缓冲,就相当于每次去银行只取1块钱,一天要去100次;有了缓冲,就是一次取100块钱,只去1次。系统调用的开销是IO性能的主要瓶颈,而缓冲流正是通过"批处理"来减少这个开销。

  • 读操作:一次性从磁盘读取大量数据到缓冲区,后续read直接从缓冲区获取,减少磁盘IO
  • 写操作:数据先写入缓冲区,当缓冲区满或调用flush()时才一次性写入磁盘
// 缓冲流工作原理示意(伪代码)classBufferedInputStreamextendsFilterInputStream{byte[]buf=newbyte[8192];// 默认8KB缓冲区intcount;// 缓冲区有效字节数intpos;// 当前读取位置publicintread(){if(pos>=count){fill();// 缓冲区空了,从底层流填充}returnbuf[pos++];}}

缓冲区大小默认是8192字节(8KB),可以通过构造方法自定义。

二、字节缓冲流

2.1 BufferedInputStream

importjava.io.BufferedInputStream;importjava.io.FileInputStream;importjava.io.IOException;publicclassBufferedInputStreamDemo{publicstaticvoidmain(String[]args){longstart,end;// 不使用缓冲流读取start=System.currentTimeMillis();try(FileInputStreamfis=newFileInputStream("D:/test/large_file.dat")){intdata;while((data=fis.read())!=-1){// 逐字节读取(极慢)}}catch(IOExceptione){e.printStackTrace();}end=System.currentTimeMillis();System.out.println("无缓冲耗时:"+(end-start)+"ms");// 使用缓冲流读取start=System.currentTimeMillis();try(BufferedInputStreambis=newBufferedInputStream(newFileInputStream("D:/test/large_file.dat"))){intdata;while((data=bis.read())!=-1){// 同样逐字节读取,但背后有缓冲区(快很多)}}catch(IOExceptione){e.printStackTrace();}end=System.currentTimeMillis();System.out.println("有缓冲耗时:"+(end-start)+"ms");}}

装饰器模式体现在此:BufferedInputStream包装了FileInputStream,在不改变原有接口的前提下增强了功能。

2.2 BufferedOutputStream

importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;publicclassBufferedOutputStreamDemo{publicstaticvoidmain(String[]args){try(BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("D:/test/buffer_output.txt"))){for(inti=0;i<1000;i++){bos.write(("第"+(i+1)+"行数据\r\n").getBytes());// 数据先写入缓冲区,减少磁盘IO次数}// flush()将缓冲区数据强制写入磁盘bos.flush();System.out.println("批量写入完成!");}catch(IOExceptione){e.printStackTrace();}}}

注意:close()方法会自动调用flush(),所以使用try-with-resources时通常不需要手动调用flush()。

三、字符缓冲流

3.1 BufferedReader

BufferedReader是字符缓冲输入流,提供了逐行读取的便捷方法:

importjava.io.BufferedReader;importjava.io.FileReader;importjava.io.IOException;publicclassBufferedReaderDemo{publicstaticvoidmain(String[]args){try(BufferedReaderbr=newBufferedReader(newFileReader("D:/test/poem.txt"))){Stringline;intlineNum=1;// readLine() 逐行读取,读到末尾返回nullwhile((line=br.readLine())!=null){System.out.println((lineNum++)+": "+line);}}catch(IOExceptione){e.printStackTrace();}}}

readLine()方法是BufferedReader独有的,其父类Reader中没有此方法,这是缓冲流提供的重要增强。

3.2 BufferedWriter

importjava.io.BufferedWriter;importjava.io.FileWriter;importjava.io.IOException;publicclassBufferedWriterDemo{publicstaticvoidmain(String[]args){try(BufferedWriterbw=newBufferedWriter(newFileWriter("D:/test/student_list.txt"))){bw.write("学号\t姓名\t成绩");bw.newLine();// 写入系统相关的换行符(跨平台兼容)bw.write("001\t张三\t95");bw.newLine();bw.write("002\t李四\t88");bw.newLine();bw.write("003\t王五\t92");System.out.println("成绩单保存成功!");}catch(IOExceptione){e.printStackTrace();}}}

bw.newLine()是跨平台的换行方法,会根据操作系统自动选择\r\n(Windows)或\n(Linux/Mac)。

四、转换流:InputStreamReader

InputStreamReader是字节流到字符流的桥梁,它将字节输入流按照指定的字符编码转换为字符输入流:

importjava.io.*;publicclassInputStreamReaderDemo{publicstaticvoidmain(String[]args){// 读取GBK编码的文件(Windows中文系统默认编码)try(InputStreamReaderisr=newInputStreamReader(newFileInputStream("D:/test/gbk_file.txt"),"GBK");BufferedReaderbr=newBufferedReader(isr)){Stringline;while((line=br.readLine())!=null){System.out.println(line);}}catch(IOExceptione){e.printStackTrace();}// 对比:FileReader使用系统默认编码,可能产生乱码try(BufferedReaderbr=newBufferedReader(newFileReader("D:/test/gbk_file.txt"))){// 如果文件是GBK编码,而系统默认是UTF-8,此处就会乱码Stringline=br.readLine();System.out.println("FileReader读取:"+line);}catch(IOExceptione){e.printStackTrace();}}}

关键理解

  • FileReader=new InputStreamReader(new FileInputStream(...))(使用系统默认编码)
  • 当需要指定非默认编码时,必须使用InputStreamReader

五、转换流:OutputStreamWriter

OutputStreamWriter是字符流到字节流的桥梁:

importjava.io.*;publicclassOutputStreamWriterDemo{publicstaticvoidmain(String[]args){Stringcontent="这是一段中文文本,包含特殊字符 © ® ™";// 写入为UTF-8编码文件try(OutputStreamWriterosw=newOutputStreamWriter(newFileOutputStream("D:/test/utf8_output.txt"),"UTF-8");BufferedWriterbw=newBufferedWriter(osw)){bw.write(content);System.out.println("UTF-8文件写入成功");}catch(IOExceptione){e.printStackTrace();}// 写入为GBK编码文件(相同内容,不同编码)try(OutputStreamWriterosw=newOutputStreamWriter(newFileOutputStream("D:/test/gbk_output.txt"),"GBK");BufferedWriterbw=newBufferedWriter(osw)){bw.write(content);System.out.println("GBK文件写入成功");}catch(IOExceptione){e.printStackTrace();}}}

六、流的装饰组合

Java IO流的核心设计模式是装饰器模式,可以通过层层嵌套组合功能:

importjava.io.*;publicclassStreamDecoratorDemo{publicstaticvoidmain(String[]args)throwsIOException{// 经典三层装饰组合// FileInputStream(底层) → InputStreamReader(解码) → BufferedReader(缓冲+按行读)try(BufferedReaderbr=newBufferedReader(newInputStreamReader(newFileInputStream("D:/test/data.txt"),"UTF-8"))){Stringline;while((line=br.readLine())!=null){System.out.println(line);}}// 写入组合// FileOutputStream(底层) → OutputStreamWriter(编码) → BufferedWriter(缓冲+换行)try(BufferedWriterbw=newBufferedWriter(newOutputStreamWriter(newFileOutputStream("D:/test/result.txt"),"UTF-8"))){bw.write("装饰器模式组合示例");bw.newLine();bw.write("灵活高效");}}}

装饰器链条使得每种流只负责单一职责:

  • FileInputStream:负责与文件交互
  • InputStreamReader:负责字节到字符的转换和编码
  • BufferedReader:负责缓冲和提供便利方法

七、性能对比实验

importjava.io.*;publicclassPerformanceTest{staticfinalStringSRC="D:/test/large.dat";staticfinalStringDEST="D:/test/copy.dat";publicstaticvoidmain(String[]args)throwsIOException{// 1. 基础字节流(最慢)testCopy("基础字节流",newFileInputStream(SRC),newFileOutputStream(DEST));// 2. 缓冲字节流testCopy("缓冲字节流",newBufferedInputStream(newFileInputStream(SRC)),newBufferedOutputStream(newFileOutputStream(DEST)));// 3. 自定义缓冲区字节流testCopy("自定义缓冲区(4KB)",newFileInputStream(SRC),newFileOutputStream(DEST),4096);}staticvoidtestCopy(Stringname,InputStreamin,OutputStreamout)throwsIOException{longstart=System.currentTimeMillis();try(in;out){byte[]buf=newbyte[1024];while(in.read(buf)!=-1){out.write(buf);}}longend=System.currentTimeMillis();System.out.println(name+" 耗时:"+(end-start)+"ms");}staticvoidtestCopy(Stringname,InputStreamin,OutputStreamout,intbufSize)throwsIOException{longstart=System.currentTimeMillis();try(in;out){byte[]buf=newbyte[bufSize];intlen;while((len=in.read(buf))!=-1){out.write(buf,0,len);}}longend=System.currentTimeMillis();System.out.println(name+" 耗时:"+(end-start)+"ms");}}

总结

本文深入讲解了Java IO中的缓冲流与转换流:

  • 缓冲流(BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter):通过内置缓冲区减少磁盘IO次数,显著提升读写性能。BufferedReader的readLine()和BufferedWriter的newLine()是常用的便捷方法
  • 转换流(InputStreamReader / OutputStreamWriter):在字节流和字符流之间建立桥梁,支持指定字符编码,解决跨编码场景下的乱码问题
  • 装饰器模式:Java IO通过层层包装组合不同流的功能,实现了高度的灵活性和可扩展性

在实际项目中,推荐始终使用缓冲流包裹基础流,并根据需要添加转换流来指定编码。

✅ 亮点总结

  • 缓冲流内部默认8KB缓冲区的工作机制:一次磁盘IO填充缓冲区,后续read()直接从内存读取,大幅减少系统调用
  • BufferedReader/BufferedWriter提供的readLine()和newLine()便捷方法,newLine()自动适配操作系统换行符
  • InputStreamReader/OutputStreamWriter作为字节-字符桥梁,核心价值在于允许显式指定字符编码(GBK/UTF-8)
  • FileReader本质是new InputStreamReader(new FileInputStream(...), 默认编码),指定编码时需显式使用转换流
  • 装饰器模式三层经典组合:底层FileStream(文件交互)→转换流(编码处理)→缓冲流(性能优化+便利方法)

适用场景

  • 大文件日志的逐行分析与数据统计,利用BufferedReader逐行读取避免一次性加载
  • 跨编码系统的数据迁移,如GBK编码的遗留系统数据转换为UTF-8存入新系统
  • 高效文件读写工具类的封装,统一对外提供按行读取、批量写入等便捷接口

扩展方向

  • 深入学习Java NIO的Channel与Buffer(非阻塞IO),理解BIO到NIO的架构升级
  • 研究Java 7 NIO.2的Files工具类(Path、Files.copy/walk等现代化文件操作API)
  • 推荐阅读:23_Java序列化与反序列化

上一篇:[Java IO流体系详解](21_Java IO流体系详解.md) | 下一篇:Java序列化与反序列化

http://www.jsqmd.com/news/966902/

相关文章:

  • ML生产化实战:从模型部署到可观测运维的完整链路
  • VNC文件传输踩坑实录:从TigerVNC到RealVNC Server的完整迁移指南(附避坑点)
  • 互动影游的Token经济革命:ibbot手机如何成为AI互动娱乐的生产节点
  • 3步掌握ToastFish:让你的Windows通知栏变身单词学习神器
  • LLaMA 1技术解析:有限度开源、RoPE与RMSNorm如何重塑大模型落地范式
  • 2026年精工智能官方联系方式公示,智能工厂规划与数字化一站式服务合作便捷入口 - 第三方测评
  • 2026年6月恒温恒湿箱厂家推荐:十大排名专业评测案例性价比高价格 - 品牌推荐
  • Python实现图像中文字字体无痕替换的五步闭环方法
  • RK3568双网口配置实战:RMII模式下的gmac0与gmac1 DTS设置详解与对比
  • 保姆级指南:用ADIsimFrequencyPlanner规划你的小数分频锁相环,避开整数边界杂散(IBS)
  • 机器学习工程化实战:从Notebook到高可用模型服务
  • 锦州黄金白银铂金回收正规资质门店TOP6 - 余生黄金回收
  • 2026年浙江CPPM报名费用怎么确认?8800元考试费教材费和冯老师联系方式 - 众智商学院官方
  • Pandas多维聚合实战:生产级数据管道的5大核心模式
  • Netty高性能的幕后功臣:深入拆解ByteBuffer与堆外内存如何联手加速网络IO
  • Modbus RTU调试避坑指南:从串口设置、CRC校验到功能码响应的常见错误排查
  • 从通信到AI:拆解FPGA在六大热门领域的真实用例与选型建议(附Cyclone IV资源表)
  • 保研推荐信别再套模板了!手把手教你用ChatGPT/Notion打造个性化文书(附真实案例拆解)
  • 2026 成都黄金回收测评:金店/典当行/线上平台价格对比 - 奢侈品交易观察员
  • 联邦学习在医疗影像分析中的隐私保护与领域泛化技术
  • 2026年厦门SCMP报名问题怎么核对?资料班期和官网400说明 - 众智商学院职业教育
  • 2026年5月上海离婚诉讼律师专业度权威排行盘点:上海继承纠纷律师/上海财产继承律师/上海起诉离婚律师/上海遗产分割律师/选择指南 - 优质品牌商家
  • PAJ7620手势传感器与Arduino Uno通信避坑指南:I2C地址、库文件安装和常见手势误识别解决
  • BetterNCM安装工具深度解析:专业级网易云插件平台部署实战
  • 企业AI落地失败真相:不是技术不行,是系统没对齐
  • 1个开源工具彻底解决Wallpaper Engine资源提取难题:RePKG完整指南
  • ML生产化实战:可观测性、弹性扩缩与闭环反馈三大核心
  • 给GIS新手的图解指南:为什么无人机定位需要ECEF和ENU坐标系转换?
  • 2026泰州AI优化技术解析与本地服务商实测对比:姜堰AI优化/姜堰geo优化/姜堰做网站/姜堰网站优化/姜堰网站建设/选择指南 - 优质品牌商家
  • Realsense D435i测距新玩法:用鼠标点击实时获取任意点深度(Python+OpenCV交互教程)