第 19 章:文件 IO
学习目标
- 掌握 Path / Files 文件操作
- 学会读写文本和二进制文件
- 避开编码和资源关闭的坑
一、Path 与 Files(NIO.2,JDK 7+)
用 Path 表示路径,Files 操作文件(新 API,推荐):
java
Path path = Path.of("data/user.txt"); // 相对路径
Path absPath = path.toAbsolutePath(); // 转绝对路径
Files.exists(path); // 是否存在
Files.size(path); // 字节数
Files.isDirectory(path); // 是否目录
Files.createDirectories(path.getParent()); // 创建多级目录
Files.deleteIfExists(path); // 不存在不抛异常为什么要用 NIO.2? 老
File类出错只返回false,不告诉你为什么;Files抛异常带详细信息。
二、读写文本文件
小文件(JDK 11+,一行搞定)
java
// 读
String content = Files.readString(Path.of("a.txt"));
// 写
Files.writeString(Path.of("b.txt"), "Hello, World!");按行读(大文件不爆内存)
java
try (Stream<String> lines = Files.lines(Path.of("big.log"))) {
lines.filter(l -> l.contains("ERROR")) // 过滤
.forEach(System.out::println);
} // 自动关闭传统写法(需要控制编码时)
java
try (BufferedReader br = Files.newBufferedReader(
Path.of("a.txt"), StandardCharsets.UTF_8)) { // ① 显式 UTF-8
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
try (BufferedWriter bw = Files.newBufferedWriter(
Path.of("b.txt"), StandardCharsets.UTF_8)) {
bw.write("Hello");
bw.newLine();
bw.write("World");
}⚠️ 坑 1:永远显式指定 UTF-8,否则用系统默认编码(Windows 中文是 GBK),跨平台会乱码。
java// ❌ 错 Files.readString(Path.of("a.txt")); // ✅ 对 Files.readString(Path.of("a.txt"), StandardCharsets.UTF_8);
三、读写二进制文件
java
// 复制文件
Files.copy(Path.of("src.png"), Path.of("dst.png"),
StandardCopyOption.REPLACE_EXISTING);
// 写二进制
byte[] data = {0x01, 0x02, 0x03};
Files.write(Path.of("a.bin"), data);
// 读二进制
byte[] read = Files.readAllBytes(Path.of("a.bin"));四、字节流 vs 字符流
| 类型 | 单位 | 适用 | 基类 |
|---|---|---|---|
| 字节流 | byte(8 位) | 图片、视频、任意二进制 | InputStream / OutputStream |
| 字符流 | char(16 位) | 文本(自动按字符集处理) | Reader / Writer |
判断标准:能用记事本打开看懂的 → 字符流;否则 → 字节流。
实际场景:用 Files 静态方法就够了,很少直接 new InputStream。但要懂底层:
java
// 等价于 Files.copy 但手动实现
try (InputStream in = Files.newInputStream(Path.of("src.png"));
OutputStream out = Files.newOutputStream(Path.of("dst.png"))) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}五、try-with-resources(必会)
所有 IO 流、数据库连接都要关——用 try-with-resources 自动关:
java
// ✅ 自动关闭
try (Connection conn = dataSource.getConnection()) {
// ...
}
// ✅ 多资源按相反顺序关闭
try (InputStream in = Files.newInputStream(src);
OutputStream out = Files.newOutputStream(dst)) {
// ...
}⚠️ 坑 2:IO 资源必须关闭,否则文件句柄不释放,打开文件太多会报 "Too many open files"。
六、实战:工具类
java
public class FileUtils {
private FileUtils() { }
// 读所有行(小文件)
public static List<String> readLines(Path path) throws IOException {
return Files.readAllLines(path, StandardCharsets.UTF_8);
}
// 追加写入
public static void append(Path path, String content) throws IOException {
Files.writeString(path, content,
StandardOpenOption.CREATE, // 不存在则创建
StandardOpenOption.APPEND); // 追加模式
}
}七、本章小结
| 要点 | 关键 |
|---|---|
| Path / Files | NIO.2 新 API,替代老 File |
| 读写文本 | Files.readString / Files.writeString |
| 编码 | 永远显式 StandardCharsets.UTF_8 |
| try-with-resources | IO 资源自动关闭,推荐 |
| 字节 vs 字符 | 二进制用字节流,文本用字符流 |
动手练习
- 文件复制:写
copyFile(Path src, Path dst),处理不存在的情况 - 读 CSV:用
Files.lines()读 CSV,过滤包含某关键字的行 - 追加日志:用
Files.writeString追加模式写日志文件,模拟日志记录
下一章:第 20 章:NIO 简述与网络编程入门 →