Java SE 25新增特性
作者:Grey
原文地址:
源码
Patterns、instanceof 和 switch 可以匹配更多类型(第三次预览)
你可以在一个模式(pattern)之后,通过 when 子句紧跟一个布尔表达式。这种用法被称为“带守卫的模式”(Guarded Pattern)。when 子句中的布尔表达式本身,就叫做“守卫”(Guard)。一个值,只有在同时满足两个条件时,才能成功匹配一个“带守卫的模式”:
- 第一,这个值要符合模式本身的要求;
- 第二,紧随其后的“守卫”表达式必须计算结果为真。
该特性在 Java SE 23 中首次作为预览功能推出,在 Java SE 24 中再度预览。在当前这个版本中,它将再一次以预览的形式发布,并且没有进行任何重大修改。
示例代码如下:
package git.snippets.jdk25; /** * When Clause * @since 25 */ public class WhenClausesTest { void main() { test("h"); doubleToRating(100d); bigNumbers(100000000000l); testSwitch(new R(33)); } static void test(Object obj) { switch (obj) { case String s when s.length() == 1 -> System.out.println("Short: " + s); case String s -> System.out.println(s); default -> System.out.println("Not a string"); } } String doubleToRating(double rating) { return switch(rating) { case 0d -> "0 stars"; case double d when d > 0d && d < 2.5d -> d + " is not good"; case double d when d >= 2.5f && d < 5d -> d + " is better"; case 5d -> "5 stars"; default -> "Invalid rating"; }; } void bigNumbers(long v) { switch (v) { case long x when x < 1_000_000L -> System.out.println("Less than a million"); case long x when x < 1_000_000_000L -> System.out.println("Less than a billion"); case long x when x < 1_000_000_000_000L -> System.out.println("Less than a trillion"); case long x when x < 1_000_000_000_000_000L -> System.out.println("Less than a quadrillion"); default -> System.out.println("At least a quadrillion"); } } record R(int x) { } // ... static void testSwitch(R r) { switch(r) { case R(int x) when x >= 5 -> System.out.println(x + " => 5"); default -> System.out.println(r.x + " < 5"); } } }
模块导入声明(第三次预览)
你可以通过一条声明,就导入一个模块(module)所导出的所有包(package)。参考JEP 511。
思考下面这个例子,它导入了四个类:
// 代码部分保留原文 import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; // ...
在这个例子中,你可以用“按需类型导入声明”(type-import-on-demand declarations,也就是我们常说的通配符 * 导入)来替换那四条“单类型导入声明”。但即便如此,你仍然需要三条声明:
import java.util.*; import java.util.function.*; import java.util.stream.*;
然而,由于 java.base 这个模块本身就导出了 java.util、java.util.function 和 java.util.stream 这几个包,所以,你现在可以把上面那三条声明,用一条模块导入声明来代替:
import module java.base;
一条模块导入声明遵循以下格式:
import module M;
它会按需导入以下所有公共的顶级类和接口:
-
模块 M 直接导出给当前模块的那些包。
-
因读取模块 M,而导致当前模块需要间接读取的其他模块所导出的包。这个机制使得一个程序可以直接使用某个模块的 API,而无需手动导入该 API 可能引用到的、来自其他模块的所有类和接口。
举个例子,import module java.sql; 这条声明,其效果等同于 import java.sql.*;,并且还加上了对 java.sql 模块间接导出包的按需导入,这些间接导出的包就包括了 java.logging 和 java.xml。
示例代码:
package git.snippets.jdk25; import module java.base; import module java.sql; public class ModuleImportDeclarationsTest { void main() throws ParserConfigurationException, SAXException { System.out.println(groupByFirstLetter("a", "abc", "bcd", "ddd", "dddc", "dfc", "bc")); // 我们不需要显式导入 SAXParserFactory 和 SAXParser,也不需要显式导入 java.xml 模块: SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); } public static Map<Character, List<String>> groupByFirstLetter(String... values) { return Stream.of(values).collect(Collectors.groupingBy(s -> Character.toUpperCase(s.charAt(0)))); } }
Compact Source Files and Instance Main Methods
这项功能最初在 Java SE 21 中第一次预览版,并在 Java SE 22、23 和 24 中持续预览。在当前版本中,它已成为一项正式功能,其标题也经过了修订。
该版本的具体更新如下:
-
用于处理基本控制台输入/输出的新类
IO,其位置已从java.io包移动到了java.lang包。这意味着java.lang包里的所有东西都会被虚拟机自动导入,所以现在每个源文件都会默认包含IO类,你无需再手动去import它了。这让代码更加干净。 -
IO类中的静态方法(比如println),将不再被默认导入到那些简化的源文件中。这和预览版的行为不同了。因此,现在调用这些方法时必须明确地写出类名,例如IO.println("Hello, world!"),而不能再像之前那样直接调用println。当然,如果你觉得每次都写IO.很麻烦,也可以通过显式的静态导入(static import)来恢复之前的便捷性。 -
IO类的底层实现也变了。它现在是基于我们非常熟悉的System.out和System.in,而不再是java.io.Console类。这是一个内部实现细节的改变,但它意味着IO类的行为会和标准的系统输入输出流更加一致。
示例代码如下:
/** * @author <a href="mailto:410486047@qq.com">Grey</a> * @date 2025/09/17 * @since 25 */ void main() { IO.println("hello world"); }