BufferedReader 效率更高,因其用8KB内存缓冲区批量读取数据,大幅减少系统调用和磁盘I/O;readLine()在缓冲区内扫描换行符,避免频繁内核态切换;合理设置缓冲区(如1MB)可进一步优化,但需权衡内存占用。

BufferedReader 效率更高,是因为它用内存缓冲区“批量搬运”数据,大幅减少昂贵的系统调用和磁盘 I/O 次数。
为什么 readLine() 比 FileReader 逐字符读快得多
FileReader 每次调用 read() 都可能触发一次底层 I/O(比如从磁盘读一个字节),而操作系统级 I/O 是极耗时的操作。BufferedReader 在构造时默认分配 8192 字节(8KB)缓冲区:它一次性从底层流(如 FileInputStream)读入一整块数据到内存,后续 readLine()、read() 等操作都从这块内存里取,直到缓冲区耗尽才再次触发 I/O。
- 假设读一个 1MB 的文本文件,用
FileReader.read()可能触发上万次系统调用;用BufferedReader默认配置通常只需约 128 次(1MB ÷ 8KB) - 这和 CPU 访问 Cache vs 主存的加速逻辑本质相同——BufferedReader 就是 I/O 层的“Cache”
-
readLine()不是简单地“跳过换行符”,它内部在缓冲区中扫描\n或\r\n,避免反复进出内核态
缓冲区大小怎么设才合理
默认 8KB 对多数场景够用,但面对超大文件(GB 级)或高吞吐日志处理,手动扩大缓冲区能进一步降低 I/O 频次。注意:不是越大越好,需权衡内存占用与收益。
- 推荐起步值:
1024 * 1024(1MB),适用于单线程读大文件 - 设置方式必须通过
InputStreamReader构造器传入尺寸,BufferedReader本身不暴露缓冲区大小参数 - 错误写法:
new BufferedReader(new FileReader("x.txt"), 65536)——FileReader不接受 size 参数,编译失败 - 正确写法:
try (BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream("data.log"), StandardCharsets.UTF_8), 1024 * 1024)) { String line; while ((line = reader.readLine()) != null) { // 处理 } }
常见性能陷阱:看似高效,实则拖慢速度
BufferedReader 本身高效,但用法不当会抵消甚至逆转优势。最典型的是混合调用与编码忽略。
立即学习“Java免费学习笔记(深入)”;
- 混用
read()和readLine():前者按字符读,后者依赖换行符定位,会导致行边界错乱、数据丢失 - 没指定编码,依赖平台默认(如 Windows 上是
GBK):中文直接变???,后续解析失败,白忙一场 - 在循环里重复调用两次
readLine():while (reader.readLine() != null) { // 第一次读,丢弃 String line = reader.readLine(); // 第二次读,实际处理的永远是下一行 → 跳行! } - 用
StringBuilder.append(line)把整个大文件拼成一个字符串:GC 压力陡增,可能 OOM;应边读边处理
比 BufferedReader 更快的替代方案存在吗
对于纯性能极限场景,有更底层选择,但代价是复杂度和可维护性上升:
-
Files.lines(Paths.get("f.txt"), UTF_8):Java 8+ 提供,底层仍是BufferedReader,语法更函数式,但不支持重置/随机访问,且未及时close()会泄露资源 -
FileChannel.map()+MappedByteBuffer:将文件直接映射到虚拟内存,绕过 JVM 堆,适合只读巨型文件;但内存占用不可控,且 Windows 下可能锁文件 - NIO
ByteBuffer配合CharsetDecoder:完全手动解码,性能最高,但要自己处理编码边界、碎片、BOM 等细节,极易出错
绝大多数业务场景,正确使用 BufferedReader(指定编码、合理缓冲区、避免混用 API)已是性价比最优解——真正的瓶颈往往不在它,而在后续字符串分割(split(" "))、数字解析(Integer.parseInt())或对象构建上。










