22_Java缓冲流与转换流 Java缓冲流与转换流文章目录Java缓冲流与转换流前言一、缓冲流的原理二、字节缓冲流2.1 BufferedInputStream2.2 BufferedOutputStream三、字符缓冲流3.1 BufferedReader3.2 BufferedWriter四、转换流InputStreamReader五、转换流OutputStreamWriter六、流的装饰组合七、性能对比实验总结✅ 亮点总结适用场景扩展方向前言上一篇我们学习了Java IO流的体系框架和基础用法。在实际开发中直接使用FileInputStream和FileReader进行逐字节或逐字符读写效率较低因为每次read操作都会触发一次磁盘IO。Java提供了缓冲流和转换流来解决性能和编码问题。缓冲流通过内置缓存区减少实际IO次数转换流则在字节流和字符流之间架起桥梁。理解这两类流是掌握Java IO装饰器模式的关键一步。如果把基础流FileInputStream等看作水管缓冲流就是水塔——先把水存储起来再批量传输转换流则是翻译官——帮你在字节和字符之间进行编码转换。两者结合使用构成了Java IO编程中最常见的实践模式。本文将深入讲解这两类流的设计原理、使用方法和性能对比。一、缓冲流的原理普通IO流每次读/写操作都需要与底层设备交互产生大量系统调用性能较低。缓冲流内部维护了一个缓冲区数组。为了理解缓冲流的重要性可以做一个类比如果没有缓冲就相当于每次去银行只取1块钱一天要去100次有了缓冲就是一次取100块钱只去1次。系统调用的开销是IO性能的主要瓶颈而缓冲流正是通过批处理来减少这个开销。读操作一次性从磁盘读取大量数据到缓冲区后续read直接从缓冲区获取减少磁盘IO写操作数据先写入缓冲区当缓冲区满或调用flush()时才一次性写入磁盘// 缓冲流工作原理示意伪代码classBufferedInputStreamextendsFilterInputStream{byte[]bufnewbyte[8192];// 默认8KB缓冲区intcount;// 缓冲区有效字节数intpos;// 当前读取位置publicintread(){if(poscount){fill();// 缓冲区空了从底层流填充}returnbuf[pos];}}缓冲区大小默认是8192字节8KB可以通过构造方法自定义。二、字节缓冲流2.1 BufferedInputStreamimportjava.io.BufferedInputStream;importjava.io.FileInputStream;importjava.io.IOException;publicclassBufferedInputStreamDemo{publicstaticvoidmain(String[]args){longstart,end;// 不使用缓冲流读取startSystem.currentTimeMillis();try(FileInputStreamfisnewFileInputStream(D:/test/large_file.dat)){intdata;while((datafis.read())!-1){// 逐字节读取极慢}}catch(IOExceptione){e.printStackTrace();}endSystem.currentTimeMillis();System.out.println(无缓冲耗时(end-start)ms);// 使用缓冲流读取startSystem.currentTimeMillis();try(BufferedInputStreambisnewBufferedInputStream(newFileInputStream(D:/test/large_file.dat))){intdata;while((databis.read())!-1){// 同样逐字节读取但背后有缓冲区快很多}}catch(IOExceptione){e.printStackTrace();}endSystem.currentTimeMillis();System.out.println(有缓冲耗时(end-start)ms);}}装饰器模式体现在此BufferedInputStream包装了FileInputStream在不改变原有接口的前提下增强了功能。2.2 BufferedOutputStreamimportjava.io.BufferedOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;publicclassBufferedOutputStreamDemo{publicstaticvoidmain(String[]args){try(BufferedOutputStreambosnewBufferedOutputStream(newFileOutputStream(D:/test/buffer_output.txt))){for(inti0;i1000;i){bos.write((第(i1)行数据\r\n).getBytes());// 数据先写入缓冲区减少磁盘IO次数}// flush()将缓冲区数据强制写入磁盘bos.flush();System.out.println(批量写入完成);}catch(IOExceptione){e.printStackTrace();}}}注意close()方法会自动调用flush()所以使用try-with-resources时通常不需要手动调用flush()。三、字符缓冲流3.1 BufferedReaderBufferedReader是字符缓冲输入流提供了逐行读取的便捷方法importjava.io.BufferedReader;importjava.io.FileReader;importjava.io.IOException;publicclassBufferedReaderDemo{publicstaticvoidmain(String[]args){try(BufferedReaderbrnewBufferedReader(newFileReader(D:/test/poem.txt))){Stringline;intlineNum1;// readLine() 逐行读取读到末尾返回nullwhile((linebr.readLine())!null){System.out.println((lineNum): line);}}catch(IOExceptione){e.printStackTrace();}}}readLine()方法是BufferedReader独有的其父类Reader中没有此方法这是缓冲流提供的重要增强。3.2 BufferedWriterimportjava.io.BufferedWriter;importjava.io.FileWriter;importjava.io.IOException;publicclassBufferedWriterDemo{publicstaticvoidmain(String[]args){try(BufferedWriterbwnewBufferedWriter(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\nWindows或\nLinux/Mac。四、转换流InputStreamReaderInputStreamReader是字节流到字符流的桥梁它将字节输入流按照指定的字符编码转换为字符输入流importjava.io.*;publicclassInputStreamReaderDemo{publicstaticvoidmain(String[]args){// 读取GBK编码的文件Windows中文系统默认编码try(InputStreamReaderisrnewInputStreamReader(newFileInputStream(D:/test/gbk_file.txt),GBK);BufferedReaderbrnewBufferedReader(isr)){Stringline;while((linebr.readLine())!null){System.out.println(line);}}catch(IOExceptione){e.printStackTrace();}// 对比FileReader使用系统默认编码可能产生乱码try(BufferedReaderbrnewBufferedReader(newFileReader(D:/test/gbk_file.txt))){// 如果文件是GBK编码而系统默认是UTF-8此处就会乱码Stringlinebr.readLine();System.out.println(FileReader读取line);}catch(IOExceptione){e.printStackTrace();}}}关键理解FileReadernew InputStreamReader(new FileInputStream(...))使用系统默认编码当需要指定非默认编码时必须使用InputStreamReader五、转换流OutputStreamWriterOutputStreamWriter是字符流到字节流的桥梁importjava.io.*;publicclassOutputStreamWriterDemo{publicstaticvoidmain(String[]args){Stringcontent这是一段中文文本包含特殊字符 © ® ™;// 写入为UTF-8编码文件try(OutputStreamWriteroswnewOutputStreamWriter(newFileOutputStream(D:/test/utf8_output.txt),UTF-8);BufferedWriterbwnewBufferedWriter(osw)){bw.write(content);System.out.println(UTF-8文件写入成功);}catch(IOExceptione){e.printStackTrace();}// 写入为GBK编码文件相同内容不同编码try(OutputStreamWriteroswnewOutputStreamWriter(newFileOutputStream(D:/test/gbk_output.txt),GBK);BufferedWriterbwnewBufferedWriter(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(BufferedReaderbrnewBufferedReader(newInputStreamReader(newFileInputStream(D:/test/data.txt),UTF-8))){Stringline;while((linebr.readLine())!null){System.out.println(line);}}// 写入组合// FileOutputStream(底层) → OutputStreamWriter(编码) → BufferedWriter(缓冲换行)try(BufferedWriterbwnewBufferedWriter(newOutputStreamWriter(newFileOutputStream(D:/test/result.txt),UTF-8))){bw.write(装饰器模式组合示例);bw.newLine();bw.write(灵活高效);}}}装饰器链条使得每种流只负责单一职责FileInputStream负责与文件交互InputStreamReader负责字节到字符的转换和编码BufferedReader负责缓冲和提供便利方法七、性能对比实验importjava.io.*;publicclassPerformanceTest{staticfinalStringSRCD:/test/large.dat;staticfinalStringDESTD:/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{longstartSystem.currentTimeMillis();try(in;out){byte[]bufnewbyte[1024];while(in.read(buf)!-1){out.write(buf);}}longendSystem.currentTimeMillis();System.out.println(name 耗时(end-start)ms);}staticvoidtestCopy(Stringname,InputStreamin,OutputStreamout,intbufSize)throwsIOException{longstartSystem.currentTimeMillis();try(in;out){byte[]bufnewbyte[bufSize];intlen;while((lenin.read(buf))!-1){out.write(buf,0,len);}}longendSystem.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-8FileReader本质是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序列化与反序列化