Skip to content
第 19 章 后端 ⏱ 10 分钟阅读

第 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 / FilesNIO.2 新 API,替代老 File
读写文本Files.readString / Files.writeString
编码永远显式 StandardCharsets.UTF_8
try-with-resourcesIO 资源自动关闭,推荐
字节 vs 字符二进制用字节流,文本用字符流

动手练习 ​

  1. 文件复制:写 copyFile(Path src, Path dst),处理不存在的情况
  2. 读 CSV:用 Files.lines() 读 CSV,过滤包含某关键字的行
  3. 追加日志:用 Files.writeString 追加模式写日志文件,模拟日志记录

下一章:第 20 章:NIO 简述与网络编程入门 →

本站基于 VitePress 构建 · 由 StackHub 团队维护