跳转至

Files 增强:readString / writeString#

版本信息

为什么需要#

JDK 8 读一个文本文件要绕一圈:Files.readAllLines(path)(返回 List<String>,要自己拼)、或 new String(Files.readAllBytes(path), charset)(手动管字节与字符集);写文本要 Files.write(path, content.getBytes(charset))。处处要操心字符集,且很多老 API 默认用平台字符集file.encoding),Windows 上常是 GBK,跨平台中文乱码的根源之一。JDK 11 的 Files.readString / writeString 一行读写,且默认 UTF-8——简洁又跨平台一致。

语法#

examples/files/真实可运行的源码(snippets 嵌入,与示例零漂移):

package com.javamodern.files;

import java.nio.file.Files;
import java.nio.file.Path;

// JDK 11:Files.readString / writeString —— 一行读写整个文本文件,默认 UTF-8。
// 另:Path.of(JDK 11)取代 Paths.get(后者只是调 Path.of,已无需再走 Paths 工具类)。
public class FilesDemo {

    /** 一行读整个文本文件(默认 UTF-8)。 */
    public static String readText(Path path) throws Exception {
        return Files.readString(path);                       // (1)
    }

    /** 一行写文本文件(默认 UTF-8,覆盖)。 */
    public static void writeText(Path path, String content) throws Exception {
        Files.writeString(path, content);                    // (2)
    }

    public static void main(String[] args) throws Exception {
        Path tmp = Files.createTempFile("demo-", ".txt");
        try {
            writeText(tmp, "Hello, Java 11!");
            System.out.println("read back = " + readText(tmp));
        } finally {
            Files.deleteIfExists(tmp);
        }
    }
}
  1. :material-lightbulb: Files.readString(path) 一行读全文(默认 UTF-8);Files.writeString(path, s) 一行写(默认覆盖、UTF-8)。顺带,JDK 11 的 Path.of("...") 取代了 Paths.get("...")

运行 FilesDemo.main 的实测输出(JDK 11):

read back = Hello, Java 11!

与 JDK 8 旧写法对比#

// 读:要么拼 List,要么手动字节解码(还得记得指定字符集)
String s = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
// 写:手动转字节
Files.write(path, s.getBytes(StandardCharsets.UTF_8));
String s = Files.readString(path);              // 默认 UTF-8
Files.writeString(path, s);                      // 默认 UTF-8、覆盖
// 需要其它字符集或追加:传第二/第三参
Files.writeString(path, s, StandardCharsets.ISO_8859_1,
        StandardOpenOption.APPEND);

底层原理#

readString 是便捷封装:内部 readAllBytes 读全部字节,再用指定 Charset(缺省 UTF-8)解码为 StringwriteString 则相反——用 Charset 编码为字节后写入,缺省 OpenOptionCREATE + WRITE + TRUNCATE_EXISTING(即覆盖)。两者都是「全量」操作(整文件入内存),适合中小文件;超大文件仍应流式读写(Files.lines / BufferedReader)。

graph LR
    P["Path"] --> RS["readString:字节 + UTF-8 解码 → String"]
    P --> WS["writeString:String + UTF-8 编码 → 字节(默认覆盖)"]
    D["默认 UTF-8"] -.取代平台字符集.- RS
    D -.取代平台字符集.- WS
    style RS fill:#bbdefb
    style WS fill:#bbdefb
    style D fill:#c8e6c9

常见坑 / 最佳实践#

  • 默认 UTF-8,不是平台字符集:与不少老 API 不同——这是好事(跨平台一致),但若代码依赖平台默认字符集的行为,迁移时要留意显式传 Charset
  • 全量读入内存readString 把整文件读成 String,别用于超大文件(GB 级);流式用 Files.lines()
  • 默认是覆盖写writeString 默认 TRUNCATE_EXISTING,会清空原文件;要追加传 StandardOpenOption.APPEND
  • Path.of 取代 Paths.getPaths.get 内部只是调 Path.of,新代码直接用 Path.of,少一层间接。

小结#

readString/writeString 是 JDK 11 文件 API 的「减负」之作:一行读写文本、默认 UTF-8,消掉 JDK 8 的字节/字符集样板与跨平台乱码隐患——日常脚本、配置读写首选。

参考#