自 Java 7 引入 java.nio.file.Files 以来,文件读写操作已大幅简化。但直到 Java 10,将整个文件内容读取为单个 String 或直接将 String 写入文件仍然需要多步转换或借助辅助工具(例如 new String(Files.readAllBytes(path), charset))。Java 11 在 Files 类中直接增加了 readStringwriteString 方法,使这类最常用的文本文件操作变得优雅且直观。本文将详细讲解这两个方法的用法、最佳实践及注意事项。

1. 方法签名与基本概念

1.1 readString

1
2
public static String readString(Path path) throws IOException
public 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 IOException
public 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;

/**
* 读取文件内容
*
* @author johnson lin
* @date 2026/4/15 00:29
*/
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); // 输出: Hello, Java 11!
} 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;

/**
* 写入字符串示例
*
* @author johnson lin
* @date 2026/4/15 00:34
*/
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();
}
}
}

/* 运行结果
写入成功: D:\src\core-java\core-java-11\data\output.txt
*/

执行后,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); // 显式指定UTF-8

3.2 控制写入行为:追加 vs 覆盖

writeStringoptions 参数允许精确控制文件打开模式。常用选项:

选项 行为
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,默认行为会覆盖原文件内容。

注意APPENDTRUNCATE_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;

/**
* 写入字符串示例
*
* @author johnson lin
* @date 2026/4/15 00:44
*/
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
// Java 7/8/9/10 方式
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.linesBufferedReader 逐行/分块处理。

5.2 目录与文件不存在

  • 读取时若文件不存在,抛出 NoSuchFileExceptionIOException 的子类)。
  • 写入时若父目录不存在,抛出 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 资源管理

readStringwriteString 内部均会自动关闭文件通道,无需手动 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 中的 DSYNCSYNC 等,确保数据同步到磁盘,但会降低性能。

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;

/**
* 配置文件示例
*
* @author johnson lin
* @date 2026/4/15 00:53
*/
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);
}
// 写入,显式使用UTF-8
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();
}
}
}
/* 运行结果
Loaded: {"version": 11, "feature": "readString"}
*/

7. 总结

Java 11 的 Files.readStringFiles.writeString 是对读写文件 API 的实用补充。它们:

  • 显著简化了文本文件的整体读写代码;
  • 默认 UTF-8 减少了平台依赖的乱码风险;
  • 支持自定义字符集和精细的打开选项;
  • 适用于大多数中小型文本文件的日常处理。

对于超大文件或需要流式处理的场景,请继续使用 Files.linesBufferedReader 等传统工具。但若您恰好需要将整个文件读入一个字符串,或一次性写入少量文本,这两个新方法无疑是最优雅的选择。

升级到 Java 11 及以上版本后,果断拥抱 readStringwriteString,让文件 I/O 代码更清晰、更安全。