Java IO 核心:BufferedReader/BufferedWriter & PrintStream/PrintWriter 技术笔记
一、笔记概述
本文聚焦 Java IO 体系中高频使用的 4 个字符/字节流工具类(BufferedReader、BufferedWriter、PrintStream、PrintWriter),从核心定位、功能差异、适用场景、代码示例、拓展技巧五个维度拆解,帮助理解其设计逻辑与实际应用,解决"该用哪个流写/读数据"的核心问题。
二、核心定位与设计目标
这 4 个类分为两大阵营,核心设计目标差异显著:
1. 缓冲流阵营(BufferedReader + BufferedWriter)
属于 Java IO 底层高效字符流,核心目标是提升字符读写效率。通过内置字符缓冲区减少磁盘/网络 IO 次数(避免每次读写都触发底层硬件操作),是处理文本文件"基础且高效"的核心工具,偏底层、偏严谨。
2. 打印流阵营(PrintStream + PrintWriter)
属于 Java IO 上层易用型流,核心目标是便捷地格式化输出数据。屏蔽了繁琐的 IO 异常处理,支持所有基本数据类型(int、double、boolean 等)和对象的直接打印,还提供格式化输出(printf),是面向"业务输出、日志打印、控制台展示"的易用工具,偏上层、偏实用。
三、逐个拆解:功能、示例、适用场景
1. BufferedReader(字符缓冲输入流)
核心功能
- 包装底层字符输入流(如 FileReader),通过缓冲区提升文本读取效率;
- 提供核心方法
readLine(),可直接按行读取文本(返回一行字符串,null 表示读取完毕),解决了"手动拼接字符读行"的繁琐; - 所有 IO 操作都会抛出
IOException,必须手动处理(try-catch 或 throws)。
代码示例(按行读取文本文件)
java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo {
public static void main(String[] args) {
// 推荐使用 try-with-resources 自动关闭流(Java 7+)
try (BufferedReader br = new BufferedReader(new FileReader("demo.txt"))) {
String line;
// 核心:readLine() 按行读取,一行一个字符串
while ((line = br.readLine()) != null) {
System.out.println("读取到内容:" + line);
}
} catch (IOException e) {
// 必须处理异常:打印异常信息或做业务兜底
System.err.println("读取文件失败:" + e.getMessage());
e.printStackTrace();
}
}
}
适用场景
- 高效读取大文本文件(如日志文件、配置文件、CSV 文件);
- 需要按行处理文本内容的场景(如解析行式数据、统计文件行数);
- 控制台输入读取(
new BufferedReader(new InputStreamReader(System.in)))。
2. BufferedWriter(字符缓冲输出流)
核心功能
- 包装底层字符输出流(如 FileWriter),通过缓冲区减少写入次数,提升文本写入效率;
- 提供
newLine()方法,自动适配不同操作系统的换行符(Windows 是 \r\n,Linux 是 \n); - 仅支持字符/字符串的写入(
write()方法),无格式化能力,所有操作抛出IOException。
代码示例(高效写入文本文件)
java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
// 写入字符串
bw.write("Java IO 缓冲流示例");
// 换行(适配系统)
bw.newLine();
// 写入字符数组
char[] chars = "BufferedWriter 高效写入".toCharArray();
bw.write(chars);
// 手动刷新缓冲区(确保数据写入磁盘,close() 也会自动刷新)
bw.flush();
} catch (IOException e) {
System.err.println("写入文件失败:" + e.getMessage());
e.printStackTrace();
}
}
}
适用场景
- 高频次写入文本数据(如批量生成文本文件、写入大量日志片段);
- 对写入性能有要求,且仅需基础字符串/字符写入的场景;
- 需要适配多系统换行符的跨平台文本写入。
3. PrintStream(字节打印流)
核心功能
- 处理字节流的格式化输出工具,底层基于字节操作;
- 支持
print()/println()/printf()方法,可直接打印所有基本数据类型(int、double 等)和对象(自动调用 toString()); - 屏蔽 IO 异常(内部捕获,不向外抛出),可通过
checkError()方法判断是否写入失败; - 支持自动刷新(构造时指定 autoFlush=true),
System.out本质就是 PrintStream(控制台输出的核心实现)。
代码示例(控制台/文件输出)
java
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamDemo {
public static void main(String[] args) {
// 1. 控制台输出(System.out 是 PrintStream 实例)
PrintStream consoleOut = System.out;
consoleOut.println("打印整数:" + 123);
consoleOut.printf("格式化输出:姓名=%s,年龄=%d\n", "张三", 25);
// 2. 写入文件(字节流方式)
try (PrintStream fileOut = new PrintStream(new FileOutputStream("printstream.txt"), true)) {
// autoFlush=true:调用 println/printf 时自动刷新缓冲区
fileOut.println("打印布尔值:" + true);
fileOut.printf("浮点格式化:%.2f\n", 3.1415);
// 检查是否有错误
if (fileOut.checkError()) {
System.err.println("写入文件出错");
}
} // 无需处理 IO 异常(PrintStream 内部捕获)
}
}
适用场景
- 控制台输出(System.out/System.err);
- 字节流场景下的格式化输出(如网络字节流、二进制文件中嵌入文本);
- 不想处理 IO 异常,追求快速输出的简单场景。
4. PrintWriter(字符打印流)
核心功能
- 处理字符流的格式化输出工具,是 PrintStream 的"字符版",更适合文本处理;
- 功能与 PrintStream 几乎一致:支持 print/println/printf、屏蔽 IO 异常、自动刷新;
- 可直接包装字符流(如 FileWriter),也可包装字节流(底层自动转换),比 PrintStream 更适配文本场景。
代码示例(文本文件格式化输出)
java
import java.io.PrintWriter;
public class PrintWriterDemo {
public static void main(String[] args) {
// try-with-resources 自动关闭流
try (PrintWriter pw = new PrintWriter("printwriter.txt", true)) {
// autoFlush=true:println/printf 触发自动刷新
pw.println("打印对象:" + new Object()); // 自动调用 toString()
pw.printf("日期格式化:%tF %<tT\n", System.currentTimeMillis());
pw.print("null 处理:");
pw.println(null); // 打印字符串 "null",不抛空指针
} // 无需处理 IO 异常
}
}
适用场景
- 文本文件的格式化输出(日志生成、报表打印);
- 字符流场景下的业务输出(如 Web 响应流、字符型网络通信);
- 追求易用性,需要快速打印多种数据类型的场景(替代 BufferedWriter + 手动类型转换)。
四、核心区别
1. 缓冲流 vs 打印流(核心阵营差异)
- 异常处理:缓冲流(BufferedReader/BufferedWriter)所有操作抛出 IOException,必须手动处理;打印流(PrintStream/PrintWriter)内部捕获异常,不向外抛出,仅通过 checkError() 检查。
- 功能侧重:缓冲流仅专注"高效读写字符",无格式化能力,仅支持字符/字符串操作;打印流侧重"格式化输出",支持所有数据类型,提供 println/printf 等便捷方法。
- 底层类型:缓冲流仅处理字符流,必须包装 Reader/Writer 子类;PrintStream 处理字节流,PrintWriter 处理字符流(可兼容字节流)。
2. PrintStream vs PrintWriter(打印流内部差异)
- 底层操作:PrintStream 基于字节,PrintWriter 基于字符;
- 文本适配:PrintWriter 更适合纯文本处理(换行、字符编码适配更友好),PrintStream 更适合字节流场景(如控制台、二进制文件);
- 编码处理:PrintWriter 构造时可直接指定字符编码,PrintStream 需通过 OutputStreamWriter 间接指定,文本场景下 PrintWriter 更易用。
3. BufferedReader vs BufferedWriter(缓冲流内部差异)
- 读写方向:BufferedReader 是输入流(读数据),核心方法 readLine();BufferedWriter 是输出流(写数据),核心方法 write()/newLine();
- 操作逻辑:两者均依赖缓冲区,Reader 是"读入缓冲区再处理",Writer 是"写入缓冲区再刷盘",都需手动 flush(或 close)确保数据生效。
五、拓展技巧 & 最佳实践
1. 流的关闭:优先使用 try-with-resources
Java 7+ 提供的 try-with-resources 语法可自动关闭实现 AutoCloseable 接口的流,无需手动调用 close(),避免资源泄漏。所有 4 个类都支持该语法,是生产环境的首选。
2. 性能优化:缓冲流的缓冲区大小
默认情况下,BufferedReader/BufferedWriter 的缓冲区是 8192 字符(8KB),若处理超大文件,可手动指定更大的缓冲区(如 64KB)提升性能:
java
// 手动指定缓冲区大小为 65536 字符(64KB)
BufferedReader br = new BufferedReader(new FileReader("bigfile.txt"), 65536);
3. 异常兜底:打印流的错误检查
PrintStream/PrintWriter 不抛异常,但需通过 checkError() 检查写入是否失败,避免"写入失败但程序无感知":
java
PrintWriter pw = new PrintWriter("test.txt");
pw.println("测试数据");
if (pw.checkError()) {
throw new RuntimeException("写入文件失败"); // 手动抛出业务异常兜底
}
pw.close();
4. 选型口诀(快速决策)
- 读文本、要高效、按行读 → 用 BufferedReader;
- 写文本、要高效、纯字符 → 用 BufferedWriter;
- 打日志、控制台、格式化、不想处理异常 → 用 PrintWriter(文本)/PrintStream(字节);
- 跨平台换行、高频写 → 优先 BufferedWriter + newLine();
- 多数据类型输出、快速开发 → 优先 PrintWriter。
六、总结
- BufferedReader/BufferedWriter 是"底层高效读写工具",偏性能、需处理异常,适合对 IO 效率有要求的文本读写场景;
- PrintStream/PrintWriter 是"上层易用打印工具",偏便捷、屏蔽异常,适合格式化输出、日志打印、控制台展示等场景;
- 文本处理优先选字符流(BufferedReader/BufferedWriter/PrintWriter),字节流场景(如控制台、二进制文件)选 PrintStream;
- 实际开发中,常组合使用(如 BufferedReader 读数据 + PrintWriter 格式化输出),兼顾效率与易用性。