416. 现代 Java I/O 最佳实践 - 高效、简洁、安全地处理文本与数据
1. 🎯 引言
在日常应用开发(特别是 Web 应用)中,我们经常会遇到以下 I/O 操作:
- 读取/写入文本文件
- 从网络读取文本、图片、
JSON - 遍历目录下的文件
- 读取 ZIP 压缩文件
- 创建临时文件或目录
Java 的 I/O API 很强大,从 Java 7 引入的 java.nio.file.Files 一直到 Java 18 的 UTF-8 默认支持,给我们带来了很多便利。
👉 需要注意的是:
- 旧的
java.io.File和java.io.BufferedReader已经过时,虽然它们在搜索结果中仍然很多,但在现代 Java 中应尽量避免。 UTF-8在Java 18之后已经是默认字符集(JEP 400),不用再手动指定,大大减少了编码问题。
2. 📖 读取文本文件
最简单的方式:
var path = Path.of("/usr/share/dict/words");
String content = Files.readString(path);
System.out.println(content);
🔎 补充说明:
-
Files.readString(path)会一次性把文件内容读入字符串。 -
在 Java 18 之前,为了避免编码问题,通常需要:
Files.readString(path, StandardCharsets.UTF_8);但现在默认就是 UTF-8,不需要再写。
3. 📑 按行读取文本文件
如果我们需要逐行处理:
List<String> lines = Files.readAllLines(path);
但对于 大文件,不建议一次性读入,而是用 流式处理:
try (Stream<String> lines = Files.lines(path)) {
lines.filter(line -> line.contains("Java"))
.forEach(System.out::println);
}
✅ 注意:Files.lines 返回的是 Stream<String>,需要 try-with-resources 来确保自动关闭。
🚫 不推荐再使用 BufferedReader.readLine()。
4. 🔤 使用 Scanner 进行分词读取
有时我们需要 按单词 拆分,而不是按行。可以用 Scanner:
try (Scanner scanner = new Scanner(path)) {
scanner.useDelimiter("\\PL+"); // 非字母作为分隔符
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
}
🚀 进阶写法:直接转成 Stream<String>
Stream<String> tokens = new Scanner(path)
.useDelimiter("\\PL+")
.tokens();
tokens.limit(10).forEach(System.out::println);
5. 🔢 读取数字与本地化问题
Scanner 也能读取数字,但要注意 本地化陷阱。
比如文本中有 100.000:
- 在美国 (US) 本地化下,它表示 100.0
- 在德国 (DE) 本地化下,它表示 100000.0
👉 如果涉及本地化,应该使用 NumberFormat:
NumberFormat nf = NumberFormat.getInstance(Locale.GERMANY);
Number num = nf.parse("100.000");
System.out.println(num); // 输出 100000
🎓 总结(课堂收尾)
- 读取整文件:
Files.readString(path) - 逐行处理:
Files.lines(path)+ 流式操作 - 分词处理:
Scanner+useDelimiter - 数字处理:注意本地化,推荐
NumberFormat
✅ 从 Java 18 开始,UTF-8 已经是默认编码,大部分时候不用再操心字符集问题。
🚫 避免使用过时的 File、BufferedReader.readLine,用 Files 和 Streams 来写现代化 I/O。