IO 流
约 911 字大约 3 分钟
布欧-Lewyon
2026-05-15
Java I/O 按数据单位分为字节流(处理二进制数据)和字符流(处理文本数据),按方向分为输入流(读取)和输出流(写入)。核心抽象是四个基类:InputStream、OutputStream、Reader、Writer。
字节流:InputStream / OutputStream
字节流以 byte 为单位读写,适用于图片、音频、视频等二进制文件。
FileInputStream —— 读取文件
try (InputStream in = new FileInputStream("input.txt")) {
int b;
while ((b = in.read()) != -1) {
System.out.print((char) b); // 逐字节读取,效率低
}
} catch (IOException e) {
e.printStackTrace();
}一次读取一个字节效率很低。用 byte[] 缓冲读取可以显著提升速度:
try (InputStream in = new FileInputStream("input.txt")) {
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
// buf[0..bytesRead-1] 是本次读取的数据
System.out.write(buf, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}FileOutputStream —— 写入文件
try (OutputStream out = new FileOutputStream("output.txt")) {
String content = "Hello, Java IO!";
out.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}FileOutputStream("file")默认覆盖模式;追加模式用new FileOutputStream("file", true)。write(byte[])比逐字节write(int)效率高得多。
字符流:Reader / Writer
字符流以 char 为单位读写,自动处理字符编码,适合纯文本文件。
try (Reader reader = new FileReader("input.txt")) {
char[] buf = new char[1024];
int charsRead;
while ((charsRead = reader.read(buf)) != -1) {
System.out.print(new String(buf, 0, charsRead));
}
} catch (IOException e) {
e.printStackTrace();
}FileWriter
try (Writer writer = new FileWriter("output.txt")) {
writer.write("你好,世界");
} catch (IOException e) {
e.printStackTrace();
}缓冲流
在流的外层包装缓冲流可以大幅减少底层 I/O 次数。BufferedReader 的 readLine() 方法在逐行读文本时最常用:
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
bw.write("第一行");
bw.newLine();
bw.write("第二行");
}- 缓冲流默认缓冲区 8192 字节(8 KB),可通过构造参数调整。
- 始终在外层包装缓冲流,内层是文件流:
BufferedReader(Reader in)。
桥接:InputStreamReader / OutputStreamWriter
将字节流转为字符流,同时指定字符编码。不指定编码时使用操作系统默认编码,跨平台时行为不可预期:
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}- 文本文件始终显式指定编码,UTF-8 是最通用的选择。
OutputStreamWriter同理:new OutputStreamWriter(new FileOutputStream("out.txt"), StandardCharsets.UTF_8)。
资源关闭与 try-with-resources
所有流都实现了 AutoCloseable,务必使用 try-with-resources 确保资源关闭:
// 正确:自动关闭
try (InputStream in = new FileInputStream("a.txt");
InputStream in2 = new FileInputStream("b.txt")) {
// 使用 in 和 in2
} catch (IOException e) {
// 处理异常
}
// 错误:finally 中关闭,代码冗长且容易漏掉异常
InputStream in = null;
try {
in = new FileInputStream("a.txt");
// ...
} finally {
if (in != null) in.close(); // close 也可能抛异常
}try-with-resources 中声明的多个资源按声明逆序关闭。catch 和 finally 块可选。
小结
- 字节流(
InputStream/OutputStream)处理二进制数据;字符流(Reader/Writer)处理文本。 - 始终使用缓冲流(
BufferedReader/BufferedWriter)包装底层流,利用readLine()逐行读取。 - 字节流转字符流时使用
InputStreamReader/OutputStreamWriter明确指定字符编码(推荐 UTF-8)。 - 所有流必须关闭:始终用
try-with-resources确保资源回收。 - 易错:
read()返回的int用-1判断流结束,但read(byte[])的返回值是实际读取的字节数(可能为 0),不要混淆两种返回值。另外,FileReader/FileWriter 使用默认编码,跨平台时文本文件可能出现乱码——始终在桥接层指定编码。 - 思考任务:实现一个简单的文件复制方法,使用
BufferedInputStream和BufferedOutputStream(8 KB 缓冲区),从一个路径复制到另一个路径。
上一节:迭代与排序
下一节:字符集与编码
