自 Java 7 引入 java.nio.file.Files 以来,文件读写操作已大幅简化。但直到 Java 10,将整个文件内容读取为单个 String 或直接将 String 写入文件仍然需要多步转换或借助辅助工具(例如 new String(Files.readAllBytes(path), charset))。Java 11 在 Files 类中直接增加了 readString 和 writeString 方法,使这类最常用的文本文件操作变得优雅且直观。本文将详细讲解这两个方法的用法、最佳实践及注意事项。
1. 方法签名与基本概念 1.1 readString 1 2 public static String readString (Path path) throws IOExceptionpublic static String readString (Path path, Charset cs) throws IOException
作用 :读取指定路径文件中的所有字符,并将其返回为一个 String。
参数 :
path – 文件路径。
cs – 可选,字符集。若不提供,默认使用 UTF-8 。
返回值 :包含文件全部内容的 String。
异常 :IOException – 如文件不存在、无读取权限或读取过程中发生 I/O 错误。
1.2 writeString 1 2 public static Path writeString (Path path, CharSequence csq, OpenOption... options) throws IOExceptionpublic static Path writeString (Path path, CharSequence csq, Charset cs, OpenOption... options) throws IOException
作用 :将指定的字符序列(CharSequence,例如 String)写入文件。
参数 :
path – 文件路径。
csq – 要写入的字符序列。
cs – 可选,字符集。默认 UTF-8。
options – 可变参数,StandardOpenOption 枚举值,控制写入行为(如创建、追加、截断等)。若不提供,等价于 CREATE, TRUNCATE_EXISTING, WRITE(即创建新文件或覆盖已有文件)。
返回值 :返回传入的 path 对象,便于链式调用。
异常 :IOException – 写入失败时抛出。
2. 基础代码示例 2.1 读取文件为字符串 假设有一个文本文件 data/hello.txt,内容为 Hello, Java 11!。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.johnson.example.files;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;public class ReadStringExample { public static void main (String[] args) { Path path = Path.of("data" , "hello.txt" ); try { String content = Files.readString(path); System.out.println(content); } catch (IOException e) { System.err.println("读取文件失败: " + e.getMessage()); } } }
2.2 写入字符串到文件 将一段文字写入 data/output.txt。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.johnson.example.files;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;public class WriteStringExample { public static void main (String[] args) { Path path = Path.of("data" , "output.txt" ); String content = "Java 11 makes file I/O simpler." ; try { Files.writeString(path, content); System.out.println("写入成功: " + path.toAbsolutePath()); } catch (IOException e) { System.err.println("写入失败: " + e.getMessage()); e.printStackTrace(); } } }
执行后,data/output.txt 将被创建(若目录不存在则会抛出 NoSuchFileException,需预先创建目录或使用 createDirectories)。
目录不存在异常信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 写入失败: data\output.txt java.nio.file.NoSuchFileException: data\output.txt at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85) at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103) at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108) at java.base/sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:235) at java.base/java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:478) at java.base/java.nio.file.Files.newOutputStream(Files.java:220) at java.base/java.nio.file.Files.write(Files.java:3425) at java.base/java.nio.file.Files.writeString(Files.java:3641) at java.base/java.nio.file.Files.writeString(Files.java:3581) at com.johnson.example.files.WriteStringExample.main(WriteStringExample.java:18) Process finished with exit code 0
3. 指定字符集与打开选项 3.1 自定义字符集 默认 UTF-8 对于多数现代应用足够,但处理遗留数据时可能需要指定其他字符集(如 ISO-8859-1、GBK)。
读取时指定字符集 :
1 2 Path path = Path.of("legacy" , "iso.txt" );String content = Files.readString(path, StandardCharsets.ISO_8859_1);
写入时指定字符集 :
1 2 String data = "中文字符测试" ;Files.writeString(path, data, StandardCharsets.UTF_8);
3.2 控制写入行为:追加 vs 覆盖 writeString 的 options 参数允许精确控制文件打开模式。常用选项:
选项
行为
StandardOpenOption.CREATE
若文件不存在则创建(默认包含)
StandardOpenOption.CREATE_NEW
创建新文件,若已存在则抛出异常
StandardOpenOption.TRUNCATE_EXISTING
文件存在时清空内容(默认包含)
StandardOpenOption.APPEND
追加内容到文件末尾
StandardOpenOption.WRITE
以写入方式打开(默认包含)
示例:追加文本
1 2 3 Path log = Path.of("app.log" );Files.writeString(log, "First line\n" , StandardOpenOption.CREATE, StandardOpenOption.APPEND); Files.writeString(log, "Second line\n" , StandardOpenOption.APPEND);
若不指定 APPEND,默认行为会覆盖原文件内容。
注意 :APPEND 与 TRUNCATE_EXISTING 互斥,同时使用会抛出 IllegalArgumentException。
如执行以下代码将会抛出错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.johnson.example.files;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.StandardOpenOption;public class WriteStringExceptionExample { public static void main (String[] args) throws IOException { Path log = Path.of("app.log" ); Files.writeString(log, "First line\n" , StandardOpenOption.CREATE, StandardOpenOption.APPEND); Files.writeString(log, "Second line\n" , StandardOpenOption.APPEND); Files.writeString(log, "Third line\n" , StandardOpenOption.APPEND, StandardOpenOption.TRUNCATE_EXISTING); } }
错误信息如下:
1 2 3 4 5 6 7 8 9 10 11 Exception in thread "main" java.lang.IllegalArgumentException: APPEND + TRUNCATE_EXISTING not allowed at java.base/sun.nio.fs.WindowsChannelFactory.newFileChannel(WindowsChannelFactory.java:166) at java.base/sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) at java.base/java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:478) at java.base/java.nio.file.Files.newOutputStream(Files.java:220) at java.base/java.nio.file.Files.write(Files.java:3425) at java.base/java.nio.file.Files.writeString(Files.java:3641) at java.base/java.nio.file.Files.writeString(Files.java:3581) at com.johnson.example.files.WriteStringExceptionExample.main(WriteStringExceptionExample.java:18) Process finished with exit code 1
4. 与传统方式的对比 Java 11 之前,将文件读为 String 的典型做法:
1 2 3 Path path = Path.of("file.txt" );String content = new String (Files.readAllBytes(path), StandardCharsets.UTF_8);
写字符串到文件:
1 Files.write(path, content.getBytes(StandardCharsets.UTF_8));
或者使用 BufferedReader / BufferedWriter:
1 2 3 4 5 6 7 8 try (BufferedReader reader = Files.newBufferedReader(path)) { StringBuilder sb = new StringBuilder (); String line; while ((line = reader.readLine()) != null ) { sb.append(line).append(System.lineSeparator()); } String content = sb.toString(); }
对比总结 :
readString / writeString 代码更简洁、意图更清晰。
自动处理字符集(默认 UTF-8),避免因忘记指定字符集而导致平台依赖问题。
内部仍基于 readAllBytes / write 实现,性能无显著差异。
对于超大文件(如数百 MB),传统流式处理更安全(避免内存溢出)。
5. 注意事项与最佳实践 5.1 文件大小与内存消耗 readString 会将整个文件内容加载到内存中。对于特大文件(例如超过 2GB,或超出 JVM 堆内存大小),应避免使用此方法。此时应使用 Files.lines 或 BufferedReader 逐行/分块处理。
5.2 目录与文件不存在
读取时若文件不存在,抛出 NoSuchFileException(IOException 的子类)。
写入时若父目录不存在,抛出 NoSuchFileException。需要先确保目录存在:
1 2 3 Path dir = Path.of("subdir" );Files.createDirectories(dir); Files.writeString(dir.resolve("file.txt" ), "content" );
5.3 显式指定字符集 虽然默认 UTF-8 符合多数场景,但为代码可读性和跨平台一致性,建议在非默认场景或团队编码规范要求下显式指定字符集。尤其当文件来源于外部系统或需要与遗留系统交互时,避免平台默认字符集(如 Windows 上的 GBK)导致乱码。
5.4 资源管理 readString 和 writeString 内部均会自动关闭文件通道,无需手动 try-with-resources。但若需要多次写入或细粒度控制,仍可使用传统的 BufferedWriter。
5.5 行分隔符 写入时,writeString 不会自动添加换行符。如需按行写入,需在字符串中包含行分隔符(System.lineSeparator() 或 \n)。例如:
1 2 String lines = String.join(System.lineSeparator(), "line1" , "line2" , "line3" );Files.writeString(path, lines);
5.6 原子性与其他特性 writeString 不保证原子性。若需要确保写入完整性,可考虑写入临时文件后使用 Files.move 原子移动。此外,writeString 支持 OpenOption 中的 DSYNC、SYNC 等,确保数据同步到磁盘,但会降低性能。
6. 完整示例:配置文件读写 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.johnson.example.files;import java.io.IOException;import java.nio.file.*;import java.nio.charset.StandardCharsets;public class ConfigFileExample { private static final Path CONFIG = Path.of("config.json" ); public static void saveConfig (String json) throws IOException { Path parent = CONFIG.getParent(); if (parent != null ) { Files.createDirectories(parent); } Files.writeString(CONFIG, json, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } public static String loadConfig () throws IOException { if (!Files.exists(CONFIG)) { return "{}" ; } return Files.readString(CONFIG, StandardCharsets.UTF_8); } public static void main (String[] args) { try { saveConfig("{\"version\": 11, \"feature\": \"readString\"}" ); String config = loadConfig(); System.out.println("Loaded: " + config); } catch (IOException e) { e.printStackTrace(); } } }
7. 总结 Java 11 的 Files.readString 和 Files.writeString 是对读写文件 API 的实用补充。它们:
显著简化了文本文件的整体读写代码;
默认 UTF-8 减少了平台依赖的乱码风险;
支持自定义字符集和精细的打开选项;
适用于大多数中小型文本文件的日常处理。
对于超大文件或需要流式处理的场景,请继续使用 Files.lines、BufferedReader 等传统工具。但若您恰好需要将整个文件读入一个字符串,或一次性写入少量文本,这两个新方法无疑是最优雅的选择。
升级到 Java 11 及以上版本后,果断拥抱 readString 和 writeString,让文件 I/O 代码更清晰、更安全。