String 与 Files:JDK 11 的文本便利方法
学完你要能自己回答
- isBlank 与 isEmpty、strip 与 trim 的边界分别是什么?
- lines() 如何处理 \n、\r\n 和末尾空行?Files.lines 返回的 Stream 需要关闭吗?
- Files.readString / writeString 默认用什么编码?为什么不等于 Charset.defaultCharset()?
四个新方法:空白、行、裁剪、重复01
JDK 11 的 String 增加了四个日常方法。它们都不是语法糖,而是把常见的字符串处理从正则和手工循环里拿出来。
| 方法 | 作用 | 边界 |
|---|---|---|
isBlank() | 是否只包含空白字符 | 使用 Character.isWhitespace;空串返回 true |
lines() | 按 \n、\r、\r\n 切分为 Stream<String> | 不保留行终止符;末尾终止符不产生空元素 |
strip() / stripLeading() / stripTrailing() | 去掉首尾空白 | 使用 Character.isWhitespace,覆盖 Unicode 空白 |
repeat(int n) | 重复 n 次 | n=0 返回空串;n<0 抛 IllegalArgumentException |
import java.util.stream.Collectors;
public class StringDemo {
public static void main(String[] args) {
String s = " \u2003Java 11\u2003 ";
System.out.println("blank=" + " \t\n".isBlank());
System.out.println("isEmpty=" + " \t\n".isEmpty());
System.out.println("stripLen=" + s.strip().length());
System.out.println("trimLen=" + s.trim().length());
System.out.println("repeat=" + "ab".repeat(3));
System.out.println("repeat0=[" + "ab".repeat(0) + "]");
System.out.println("lines=" + "a\nb\r\nc".lines().collect(Collectors.toList()));
System.out.println("trailing=" + "a\n".lines().collect(Collectors.toList()));
System.out.println("blankLine=" + "a\n\nb".lines().collect(Collectors.toList()));
try {
"ab".repeat(-1);
} catch (IllegalArgumentException e) {
System.out.println("repeat(-1)=" + e.getClass().getSimpleName());
}
}
}
blank=true isEmpty=false stripLen=7 trimLen=9 repeat=ababab repeat0=[] lines=[a, b, c] trailing=[a] blankLine=[a, , b] repeat(-1)=IllegalArgumentException
lines() 在遇到末尾换行时不会额外产生一个空串:"a\n".lines() 只有一个元素 a。但两个换行之间确实有空行,"a\n\nb" 会得到 [a, , b]。
strip 与 trim:Unicode 空白的差别02
trim() 的规则是去掉首尾所有码点小于等于 U+0020 的字符;strip() 使用 Character.isWhitespace,能处理更多 Unicode 空白,例如 U+2003 EM SPACE。
String s = " \u2003Java 11\u2003 ";
System.out.println(s.strip().length()); // 7
System.out.println(s.trim().length()); // 9
System.out.println("\u00A0".isBlank()); // false
System.out.println("\u00A0x\u00A0".strip().length()); // 3
7 9 false 3
U+00A0 NO-BREAK SPACE 是一个例外:它是空格字符,但 Character.isWhitespace 返回 false,所以 isBlank() 不认为它空白,strip() 也不会去掉它。需要处理 NBSP 时要自己替换或使用正则。
| 输入 | trim | strip | 说明 |
|---|---|---|---|
| 首尾普通空格 | 去掉 | 去掉 | 行为一致 |
| U+2003 EM SPACE | 保留 | 去掉 | strip 覆盖 Unicode 空白 |
| U+00A0 NBSP | 保留 | 保留 | 不是 Character.isWhitespace |
| 制表符 / 换行 | 去掉 | 去掉 | 都属于空白 |
Files.readString 与 writeString03
JDK 11 的 Files 增加了整文件读写的快捷方法:readString(Path) 与 writeString(Path, CharSequence, OpenOption...)。它们默认使用 UTF-8,不是平台默认编码。
import java.nio.charset.MalformedInputException;
import java.nio.file.*;
public class FilesDemo {
public static void main(String[] args) throws Exception {
Path p = Path.of("demo.txt");
Files.writeString(p, "line1\nline2\n");
System.out.println("read=" + Files.readString(p).lines().count());
Files.writeString(p, "line3\n", StandardOpenOption.APPEND);
System.out.println("lines=" + Files.readAllLines(p));
System.out.println("size=" + Files.size(p));
Path bad = Path.of("bad.txt");
Files.write(bad, new byte[]{(byte) 0xC3, (byte) 0x28});
try {
Files.readString(bad);
} catch (MalformedInputException e) {
System.out.println("bad=" + e.getClass().getSimpleName());
}
}
}
read=2 lines=[line1, line2, line3] size=18 bad=MalformedInputException
readString一次性把整个文件读进内存;大文件应使用Files.lines或BufferedReader。writeString的默认打开选项是 CREATE、TRUNCATE_EXISTING、WRITE;追加要显式传StandardOpenOption.APPEND。readString用 UTF-8 严格解码,遇到非法字节抛MalformedInputException;不会静默替换。Files.lines返回的 Stream 持有文件句柄,必须用 try-with-resources 关闭;String.lines()不需要关闭。- 需要其他编码时用
readString(path, charset)/writeString(path, text, charset, options)。
try (var lines = Files.lines(path)) {
lines.forEach(System.out::println);
}
Files.readAllLines 也返回 List 并一次性读入内存;它和 readString 的差别只是按行切分,不是流式。默认编码:JDK 11 仍然是平台相关04
这是 JDK 11 最容易被忽略的编码边界:Files.readString / writeString 固定 UTF-8,但 new String(bytes)、FileReader、PrintWriter 等使用 Charset.defaultCharset()。JDK 11 的默认编码取决于操作系统和启动参数;Windows 中文环境常见 GBK。
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
public class EncodingDemo {
public static void main(String[] args) throws Exception {
Path p = Path.of("cn.txt");
Files.writeString(p, "中文");
byte[] bytes = Files.readAllBytes(p);
System.out.println("bytes=" + bytes.length);
System.out.println("readString=" + Files.readString(p).length());
System.out.println("utf8=" + new String(bytes, StandardCharsets.UTF_8).length());
System.out.println("gbk=" + new String(bytes, Charset.forName("GBK")).length());
}
}
bytes=6 readString=2 utf8=2 gbk=3
javac -encoding UTF-8 EncodingDemo.java,否则编译器按 GBK 读取源文件,字面量会先被误读,再写出 9 个字节,输出就不再是下面这组值。Files.writeString 写入 6 个 UTF-8 字节;按 UTF-8 解码得到 2 个字符,按 GBK 解码得到 3 个字符。Files.readString 的结果与 UTF-8 一致,说明它没有使用平台默认编码。
| API | JDK 11 编码 | 说明 |
|---|---|---|
Files.readString(Path) | UTF-8 | 严格解码,非法输入抛 MalformedInputException |
Files.writeString(Path, CharSequence) | UTF-8 | 默认 CREATE / TRUNCATE_EXISTING / WRITE |
new String(byte[]) | 平台默认 | Windows 中文环境常见 GBK |
FileReader / FileWriter | 平台默认 | JDK 11 没有无参 UTF-8 构造器 |
System.out | 平台默认 | 重定向时尤其容易乱码 |
Charset.defaultCharset() 是 UTF-8;跨平台读写文件时显式传 charset,或统一使用 Files 的 UTF-8 重载。别把 12 / 15 / 18 的方法记成 1105
String 和 Files 在后续版本还在加方法。下面这些经常被误认为 JDK 11 可用,实际不在 11。
| API | 最早版本 | JDK 11 | 用途 |
|---|---|---|---|
String.isBlank / lines / strip / repeat | 11 | 可用 | 本文主题 |
String.indent / transform | 12 | 不可用 | 缩进、函数式转换 |
String.formatted | 15 | 不可用 | 格式化占位符 |
文本块 """ | 15 | 不可用 | 多行字符串 |
Files.readString / writeString | 11 | 可用 | 整文件读写 |
Files.mismatch | 12 | 不可用 | 比较两个文件 |
Files.writeString 的 UTF-8 默认 | 11 | 可用 | 与平台默认编码无关 |
// JDK 15+,JDK 11 编译失败
String json = """
{"name": "java"}
""";
版本边界BOUNDARY
这张表比 API 本身更值得记。写代码时先确认你的运行时版本。
| 特性 / API | 状态 | 说明 |
|---|---|---|
| String.isBlank / lines / strip / repeat | 11 正式 | 不是 9/10;strip 使用 Character.isWhitespace。 |
| Files.readString / writeString | 11 正式 | 默认 UTF-8;不是平台默认编码。 |
| Path.of | 11 正式 | 替代 Paths.get。 |
| String.transform / indent | 12 正式 | JDK 11 不可用。 |
| String.formatted / 文本块 | 15 正式 | JDK 11 不可用;文本块 13/14 预览。 |
| 默认字符集 UTF-8 | 18 正式 | JEP 400;JDK 11 仍平台相关。 |
| Files.mismatch | 12 正式 | JDK 11 不可用。 |
费曼自检SELF CHECK
先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。