JDK 21 新增 API
学完你要能自己回答
- Math.clamp 在 min > max 或 NaN 时分别是什么行为?
- String 的区间 indexOf 和「先 substring 再 indexOf」有什么区别?
- splitWithDelimiters 的 limit 参数如何影响结果?
- KEM 是加密算法吗?它和 TLS、密钥交换是什么关系?
Math.clamp:把值限制在区间内01
JDK 21 新增 Math.clamp,把值限制在 [min, max] 内。它比手写 Math.min(Math.max(v, min), max) 少一次拼写错误,也更清楚地表达意图。
| 重载 | 返回 | 说明 |
|---|---|---|
clamp(long value, int min, int max) | int | 没有 int,int,int 重载;int 实参会走这个版本 |
clamp(long value, long min, long max) | long | long 区间 |
clamp(float value, float min, float max) | float | 浮点区间 |
clamp(double value, double min, double max) | double | 浮点区间 |
System.out.println(Math.clamp(15, 0, 10)); // 10
System.out.println(Math.clamp(-5, 0, 10)); // 0
System.out.println(Math.clamp(5, 0, 10)); // 5
System.out.println(Math.clamp(1_000_000_000_000L, 0L, 10L)); // 10
System.out.println(Math.clamp(Double.NaN, 0.0, 1.0)); // NaN
// Math.clamp(5, 10, 0); // IllegalArgumentException
- 区间是闭区间:等于 min 或 max 时原样返回。
NaN原样返回NaN,不会被夹到 min 或 max。min > max会抛IllegalArgumentException,不会自动交换。- 浮点
-0.0走Math.max/min语义时可能变成+0.0,对符号敏感的场景不要依赖 clamp。
String 区间查找:indexOf(str, begin, end)02
JDK 21 给 String 增加了带范围的 indexOf(String, int, int):只在 [beginIndex, endIndex) 内查找,返回绝对下标,找不到返回 -1。它避免了先 substring 再查找产生的临时字符串。
String s = "abcabc";
System.out.println(s.indexOf("bc", 0, 3)); // 1
System.out.println(s.indexOf("bc", 2, 6)); // 4
System.out.println(s.indexOf("bc", 0, 1)); // -1
System.out.println(s.indexOf("", 3, 3)); // 3 —— 空串在 begin 处命中
区间参数有严格约束:0 <= beginIndex <= endIndex <= length()。违反任何一条都抛 StringIndexOutOfBoundsException,而不是返回 -1。
String s = "abcdcef";
// s.indexOf("bc", 4, 2); // begin > end
// s.indexOf("bc", -1, 3); // begin < 0
// s.indexOf("bc", 1, 99); // end > length
// 三者都抛 StringIndexOutOfBoundsException
System.out.println(s.indexOf("bc", 1, s.length())); // 1
// s.indexOf(null, 0, 2); // NullPointerException
注意:JDK 21 只增加了区间版 indexOf,没有区间版 lastIndexOf。要在区间内找最后一次出现,可以先用 lastIndexOf(str, end - 1) 找到,再检查是否落在 begin 之后。
String s = "abcabc";
int begin = 2, end = 6;
int i = s.lastIndexOf("bc", end - 1); // 4
boolean inRange = i >= begin; // true
System.out.println(i + " / " + inRange);
splitWithDelimiters:保留分隔符03
普通 split 只返回被切开的片段,分隔符丢失。JDK 21 的 splitWithDelimiters(regex, limit) 把匹配到的分隔符也作为独立元素保留下来,适合重建原文、解析简单表达式或保留格式。
System.out.println(Arrays.toString("a,b;c".splitWithDelimiters("[,;]", 0)));
// [a, ,, b, ;, c]
System.out.println(Arrays.toString("a,b;c".splitWithDelimiters("[,;]", 2)));
// [a, ,, b;c]
System.out.println(Arrays.toString("a,b;c".splitWithDelimiters("[,;]", 1)));
// [a,b;c]
System.out.println(Arrays.toString("".splitWithDelimiters(",", 0)));
// []
| limit | 行为 | 结果长度 |
|---|---|---|
0 | 尽量切分,保留末尾空段与分隔符 | 不限制 |
1 | 完全不切分,返回整个输入 | 1 |
2 | 只应用一次分隔符,最后一段包含剩余未切分内容 | 最多 2 |
n > 0 | 最多产生 n 个元素,最后一段保留剩余内容 | 最多 n |
和 split 一样,分隔符参数是正则。按 .、|、* 等字符切分时要转义,或用 Pattern.quote。
splitWithDelimiters 不改变 split 的既有语义;普通 split 传入负 limit 时会保留尾部空段。需要重建原文时,用 String.join("", parts) 即可。StringBuilder.repeat 与 Character emoji04
StringBuilder.repeat(CharSequence, int) 和 repeat(int codePoint, int count) 在 JDK 21 加入,直接在可变缓冲区里重复追加,不会先创建一个重复后的 String。
StringBuilder sb = new StringBuilder("ab");
sb.repeat("-", 3);
System.out.println(sb); // ab---
System.out.println(new StringBuilder().repeat('x', 3)); // xxx
System.out.println("[" + new StringBuilder().repeat("x", 0) + "]"); // []
// new StringBuilder().repeat("x", -1); // IllegalArgumentException
Character 新增了一组按码点判断 emoji 属性的方法。它们判断的是 Unicode 属性,不是「用户看到的一个字符」。
| 方法 | 判断什么 | 示例 |
|---|---|---|
isEmoji(int) | 是否是 emoji 字符 | 😀 → true,'A' → false |
isEmojiPresentation(int) | 是否默认以 emoji 形式呈现 | 😀 → true |
isEmojiModifier(int) | 是否是肤色修饰符 | 🏻 → true |
isEmojiModifierBase(int) | 是否能接肤色修饰符 | 👍 → true |
isEmojiComponent(int) | 是否是 emoji 组件(ZWJ、变体选择符等) | ZWJ → true |
isExtendedPictographic(int) | 是否是扩展象形字符 | 😀 → true |
System.out.println(Character.isEmoji(0x1F600)); // true
System.out.println(Character.isEmojiModifierBase(0x1F44D)); // true
System.out.println(Character.isEmojiComponent(0x200D)); // true
System.out.println(Character.isEmoji('A')); // false
BreakIterator 或 ICU,不能只靠这些单码点判断。KEM:标准的密钥封装接口05
JEP 452 引入 javax.crypto.KEM,把「用公钥封装出一个共享密钥」抽象成标准 API。它不是新的加密算法,而是让 DHKEM 这类密钥封装机制可以通过 JCA provider 统一调用。JDK 21 的 SunJCE 提供了 DHKEM。
KeyPair kp = KeyPairGenerator.getInstance("X25519").generateKeyPair();
KEM kem = KEM.getInstance("DHKEM");
// 发送方:用接收方公钥封装
KEM.Encapsulator encapsulator = kem.newEncapsulator(kp.getPublic());
KEM.Encapsulated encapsulated = encapsulator.encapsulate();
byte[] ciphertext = encapsulated.encapsulation();
SecretKey senderKey = encapsulated.key();
// 接收方:用私钥解封装
KEM.Decapsulator decapsulator = kem.newDecapsulator(kp.getPrivate());
SecretKey receiverKey = decapsulator.decapsulate(ciphertext);
System.out.println(encapsulator.providerName()); // SunJCE
System.out.println(encapsulator.secretSize()); // 32
System.out.println(Arrays.equals(senderKey.getEncoded(), receiverKey.getEncoded())); // true
provider=SunJCE secret size=32 encapsulation size=32 keys match=true
encapsulation()是要传给接收方的密文;key()是双方共享的密钥。newEncapsulator需要公钥,newDecapsulator需要私钥;DHKEM 使用 X25519 密钥对。- 算法名由 provider 决定。换 provider 时,
DHKEM是否可用、参数要求都可能不同。 - KEM 是协议积木,不是完整协议。TLS、SSH 等仍然应该使用完整协议实现。
HttpClient 支持 try-with-resources06
JDK 21 让 HttpClient 实现了 AutoCloseable,并新增 close()、shutdown()、shutdownNow()、awaitTermination()、isTerminated()。短生命周期的命令行工具可以用 try-with-resources 确保连接池和执行器被释放。
try (HttpClient client = HttpClient.newHttpClient()) {
HttpRequest request = HttpRequest.newBuilder(
URI.create("https://example.com/health")).GET().build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
}
// 离开 try 块后 client 已关闭,不能继续复用
HttpClient 仍然是线程安全、可复用的。长生命周期的服务不要每个请求 new 一个,也不要在请求结束后关闭共享客户端;只在应用关闭或工具退出时关闭。
版本边界BOUNDARY
这张表比 API 本身更值得记。写代码时先确认你的运行时版本。
| 特性 / API | 状态 | 说明 |
|---|---|---|
| Math.clamp | 21 正式 | int 实参走 clamp(long,int,int);NaN 原样返回,min>max 抛 IAE。 |
| String.indexOf(String,int,int) | 21 正式 | 只有 indexOf 有区间版,没有区间版 lastIndexOf。 |
| String.splitWithDelimiters | 21 正式 | 保留分隔符;limit 语义与 split 一致。 |
| StringBuilder.repeat | 21 正式 | CharSequence 与 codePoint 两个重载。 |
| Character emoji 判断 | 21 正式 | 按码点判断 Unicode 属性,不处理字素簇。 |
| KEM API | 21 正式 | JEP 452;SunJCE 提供 DHKEM。 |
| HttpClient AutoCloseable | 21 新增 | close / shutdown / awaitTermination。 |
| FFM API | 21 预览,22 正式 | 21 需要 --enable-preview 与 --enable-native-access。 |
| Scoped Values | 21 预览,25 正式 | 21 的 runWhere/callWhere/getWhere 在后续预览中被移除。 |
| String Templates | 21 预览,23 撤回 | 不要在生产代码中使用。 |
费曼自检SELF CHECK
先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。