Java的演进节奏从JDK9开始显著加快,每半年一个新版本的发布节奏为Java带来了大量的新特性。
其中包括令人瞩目的虚拟线程、记录类型等"明星特性",也有很多不太引人注意但同样实用的小功能。
本文整理了29个JDK9到JDK21中值得掌握的实用特性,帮助你编写更简洁、高效、安全的Java代码。
JDK 9 (2017年9月):模块化与API增强
1. 集合工厂方法:一行代码创建不可变集合
在JDK9之前,创建小型不可变集合相当繁琐,现在只需要一行代码:
// 旧方式
List<String> list = Collections.unmodifiableList(Arrays.asList("Java", "Kotlin", "Scala"));
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
put("Java", 1995);
put("Kotlin", 2011);
put("Scala", 2004);
}});
// JDK9方式
List<String> list = List.of("Java", "Kotlin", "Scala");
Set<String> set = Set.of("Java", "Kotlin", "Scala");
Map<String, Integer> map = Map.of(
"Java", 1995,
"Kotlin", 2011,
"Scala", 2004
);
对于更多键值对,可以使用Map.ofEntries()
:
Map<String, Integer> largeMap = Map.ofEntries(
Map.entry("Java", 1995),
Map.entry("Kotlin", 2011),
Map.entry("Scala", 2004),
Map.entry("Groovy", 2003)
// ...可以有更多entries
);
实用场景:常量定义、配置集合、测试数据准备、API返回不可变结果。
2. 私有接口方法:接口代码复用不再尴尬
JDK8引入了接口默认方法,JDK9进一步允许接口拥有私有方法,便于在接口内部复用代码:
public interface FileProcessor {
// 公共抽象方法
void process(Path path);
// 默认方法
default void processFile(String fileName) {
validateFileName(fileName); // 复用私有方法进行验证
process(Path.of(fileName));
log("Processed file: " + fileName);
}
default void processDirectory(String dirName) {
validateFileName(dirName); // 复用相同的验证逻辑
try (Stream<Path> paths = Files.list(Path.of(dirName))) {
paths.forEach(this::process);
} catch (IOException e) {
handleException(e); // 复用私有方法处理异常
}
log("Processed directory: " + dirName);
}
// 私有方法 - 供默认方法使用
private void validateFileName(String fileName) {
if (fileName == null || fileName.isEmpty()) {
throw new IllegalArgumentException("File name cannot be empty");
}
}
// 私有静态方法
private static void log(String message) {
System.out.println("[" + LocalDateTime.now() + "] " + message);
}
private void handleException(Exception e) {
log("Error: " + e.getMessage());
}
}
实用场景:API设计、接口默认方法逻辑复用、框架开发。
3. Stream API增强:流操作更灵活
JDK9为Stream API增加了几个实用方法:
// 1. takeWhile - 从头开始获取元素,直到条件不满足
Stream.of(2, 4, 6, 8, 9, 10, 12)
.takeWhile(n -> n % 2 == 0) // 结果: [2, 4, 6, 8]
.forEach(System.out::println);
// 2. dropWhile - 从头开始丢弃元素,直到条件不满足
Stream.of(2, 4, 6, 8, 9, 10, 12)
.dropWhile(n -> n % 2 == 0) // 结果: [9, 10, 12]
.forEach(System.out::println);
// 3. ofNullable - 安全创建单元素流,处理null值
Stream.ofNullable(null).count(); // 0
Stream.ofNullable("Java").count(); // 1
// 4. iterate方法重载 - 带终止条件的迭代
// 旧方式需要使用limit或filter来限制
Stream.iterate(1, n -> n * 2)
.limit(5)
.forEach(System.out::println);
// 新方式更直接
Stream.iterate(1, n -> n < 100, n -> n * 2)
.forEach(System.out::println); // 1, 2, 4, 8, 16, 32, 64
实用场景:数据处理管道、复杂条件过滤、有界数据生成。
4. InputStream.transferTo():流复制不再繁琐
在JDK9之前,在流之间复制数据需要手动处理缓冲区,现在只需要一行代码:
// 旧方式 - 冗长且易错
try (InputStream is = new FileInputStream("source.txt");
OutputStream os = new FileOutputStream("target.txt")) {
byte[] buffer = new byte[8192];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
}
// JDK9方式 - 简洁明了
try (InputStream is = new FileInputStream("source.txt");
OutputStream os = new FileOutputStream("target.txt")) {
is.transferTo(os); // 一行代码搞定
}
实用场景:文件复制、网络数据传输、流处理。
5. 改进的Process API:管理系统进程更容易
JDK9大幅增强了Process API,让Java程序与系统进程的交互更加强大:
// 获取当前进程
ProcessHandle current = ProcessHandle.current();
System.out.println("Current PID: " + current.pid());
// 获取进程信息
current.info().user().ifPresent(user -> System.out.println("User: " + user));
current.info().commandLine().ifPresent(cmd -> System.out.println("Command: " + cmd));
current.info().startInstant().ifPresent(start -> System.out.println("Start time: " + start));
current.info().totalCpuDuration().ifPresent(cpu -> System.out.println("CPU time: " + cpu));
// 列出所有子进程
current.children().forEach(child -> System.out.println("Child PID: " + child.pid()));
// 列出所有进程
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command().isPresent())
.forEach(ph -> System.out.println(ph.pid() + ": " + ph.info().command().get()));
// 启动并等待进程完成
ProcessBuilder pb = new ProcessBuilder("ls", "-l");
Process process = pb.start();
ProcessHandle handle = process.toHandle();
boolean terminated = handle.onExit().thenAccept(p ->
System.out.println("Process " + p.pid() + " terminated")
).isDone();
实用场景:系统管理工具、守护进程、执行外部命令、监控应用。
JDK 10 (2018年3月):局部变量推断
6. 局部变量类型推断(var):告别冗长的变量声明
JDK10引入了局部变量类型推断,使用var
关键字让编译器推断变量类型:
// 旧方式 - 类型重复且冗长
HashMap<String, List<Customer>> customersByCity = new HashMap<>();
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
URLConnection connection = new URL("https://example.com").openConnection();
// JDK10方式 - 简洁明了
var customersByCity = new HashMap<String, List<Customer>>();
var reader = new BufferedReader(new FileReader("data.txt"));
var connection = new URL("https://example.com").openConnection();
// 在for循环中特别有用
for (var entry : customersByCity.entrySet()) {
var city = entry.getKey();
var customers = entry.getValue();
// ...
}
注意事项:
-
var
只能用于局部变量,不能用于字段、方法参数或返回类型 - 声明时必须初始化变量
- 不要过度使用,当类型不明显时应该明确声明类型
最佳实践:
// 好的用法 - 类型明确
var customers = new ArrayList<Customer>();
var entry = Map.entry("key", "value");
// 避免的用法 - 类型不明确
var result = getResult(); // 返回类型不明显
var x = 1; // 基本类型推荐显式声明
实用场景:复杂泛型类型、匿名类、lambda表达式中的变量。
7. 不可修改集合的复制方法:集合转换更安全
JDK10为集合框架增加了copyOf
方法,创建不可修改的集合副本:
// 原始集合
List<String> original = new ArrayList<>(List.of("Java", "Kotlin", "Scala"));
Set<Integer> originalSet = new HashSet<>(Set.of(1, 2, 3));
Map<String, Integer> originalMap = new HashMap<>(Map.of("one", 1, "two", 2));
// 创建不可修改的副本
List<String> copy = List.copyOf(original);
Set<Integer> copiedSet = Set.copyOf(originalSet);
Map<String, Integer> copiedMap = Map.copyOf(originalMap);
// 修改原集合不影响副本
original.add("Groovy");
System.out.println(original); // [Java, Kotlin, Scala, Groovy]
System.out.println(copy); // [Java, Kotlin, Scala]
// 尝试修改副本会抛出异常
try {
copy.add("Clojure"); // 抛出 UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify immutable copy");
}
与Collections.unmodifiableList()
不同,List.copyOf()
会创建一个全新的集合,如果原集合已经是不可修改的,则可能直接返回原集合而不是副本。
实用场景:防御性编程、返回安全的集合副本、创建常量集合。
JDK 11 (2018年9月):长期支持版功能增强
8. String新方法:文本处理得心应手
JDK11为String类增加了几个实用方法:
// 1. lines() - 按行分割字符串
String multiline = "Java\nKotlin\nScala";
multiline.lines()
.map(String::toUpperCase)
.forEach(System.out::println);
// 2. strip(), stripLeading(), stripTrailing() - 去除空白字符
String text = " Hello World ";
System.out.println(">" + text.strip() + "<"); // >Hello World<
System.out.println(">" + text.stripLeading() + "<"); // >Hello World <
System.out.println(">" + text.stripTrailing() + "<"); // > Hello World<
// strip()与trim()的区别: strip()识别更多的Unicode空白字符
String unicodeWhitespace = "\u2005Hello\u2005";
System.out.println(">" + unicodeWhitespace.trim() + "<"); // >⠀Hello⠀<
System.out.println(">" + unicodeWhitespace.strip() + "<"); // >Hello<
// 3. isBlank() - 检查字符串是否为空白
System.out.println(" ".isBlank()); // true
System.out.println("".isBlank()); // true
System.out.println(" a ".isBlank()); // false
// 4. repeat() - 重复字符串
String star = "*";
System.out.println(star.repeat(10)); // **********
System.out.println("=".repeat(20)); // ====================
实用场景:处理用户输入、解析文本文件、构建格式化输出。
9. Files新方法:文件读写一步到位
JDK11为Files类添加了几个便捷方法:
// 读取文件为String
String content = Files.readString(Path.of("config.json"));
// 写入String到文件
Files.writeString(Path.of("output.txt"), "Hello Java 11!");
// 使用指定编码
String content = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8);
Files.writeString(Path.of("log.txt"), "Logged at: " + LocalDateTime.now(),
StandardCharsets.UTF_8);
// 写入字符串集合
List<String> lines = List.of("Line 1", "Line 2", "Line 3");
Files.write(Path.of("lines.txt"), lines);
实用场景:读取配置文件、生成报告、日志记录、快速文件I/O。
10. 标准HTTP客户端:现代化网络请求
JDK11将HTTP Client从孵化模块升级为标准API,提供了现代化的HTTP客户端:
// 创建HTTP客户端
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
// 构建GET请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.header("User-Agent", "Java 11 HttpClient")
.GET()
.build();
// 同步发送请求,接收JSON响应
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Status code: " + response.statusCode());
System.out.println("Body: " + response.body());
// POST请求示例
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{"name": "Java"}"))
.build();
// 异步发送请求
client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
// 处理JSON响应(需要JSON库)
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(body -> {
// 使用Jackson或Gson解析JSON
return body;
})
.thenAccept(System.out::println)
.join();
实用场景:RESTful API调用、微服务通信、网络爬虫、数据集成。
JDK 12 (2019年3月):语言和库的改进
11. String.transform():链式字符串处理
JDK12为String类添加了transform
方法,支持链式函数转换:
// 传统方式
String original = "hello, world!";
String result = original.toUpperCase();
result = result.substring(0, 5);
result = result + "...";
// 使用transform方式
String result = "hello, world!"
.transform(String::toUpperCase)
.transform(s -> s.substring(0, 5))
.transform(s -> s + "...");
System.out.println(result); // HELLO...
// 复杂转换
String parsed = "{ "name": "John", "age": 30 }"
.transform(json -> {
// 解析JSON
// 此处简化,实际应使用Jackson等
return json.substring(json.indexOf("name") + 7, json.indexOf("age") - 3);
})
.transform(String::trim)
.transform(String::toUpperCase);
System.out.println(parsed); // JOHN
对于多步转换特别有用,提高了代码可读性。
实用场景:数据转换链、复杂字符串处理、函数式数据处理管道。
12. Compact Number Formatting:数字的可读性表示
JDK12引入了紧凑数字格式化功能,可以将大数字格式化为更易读的形式:
// 创建简短格式的格式化器
NumberFormat shortFormatter = NumberFormat.getCompactNumberInstance(
Locale.US, NumberFormat.Style.SHORT);
// 格式化数字
System.out.println(shortFormatter.format(1000)); // 1K
System.out.println(shortFormatter.format(1500)); // 2K (四舍五入)
System.out.println(shortFormatter.format(1000000)); // 1M
System.out.println(shortFormatter.format(1000000000)); // 1B
// 长格式
NumberFormat longFormatter = NumberFormat.getCompactNumberInstance(
Locale.US, NumberFormat.Style.LONG);
System.out.println(longFormatter.format(1000)); // 1 thousand
System.out.println(longFormatter.format(1000000)); // 1 million
// 其他语言的格式化
NumberFormat germanFormatter = NumberFormat.getCompactNumberInstance(
Locale.GERMANY, NumberFormat.Style.SHORT);
System.out.println(germanFormatter.format(1000)); // 1.000
NumberFormat chineseFormatter = NumberFormat.getCompactNumberInstance(
Locale.CHINA, NumberFormat.Style.SHORT);
System.out.println(chineseFormatter.format(1000)); // 1千
System.out.println(chineseFormatter.format(1000000)); // 100万
// 自定义精度
shortFormatter.setMaximumFractionDigits(1);
System.out.println(shortFormatter.format(1234)); // 1.2K
System.out.println(shortFormatter.format(1500)); // 1.5K
实用场景:用户界面显示、仪表盘开发、数据可视化、国际化应用。
JDK 14 (2020年3月):友好错误信息与语言改进
13. 友好的NullPointerException:告别空指针调试噩梦
JDK14增强了NullPointerException,异常消息中会准确指出哪个变量是null:
// 假设有这样的代码
User user = null;
String city = user.getAddress().getCity();
在JDK14之前,你会得到一个简单的消息:
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:5)
在JDK14及之后,异常消息变得非常具体:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "User.getAddress()" because "user" is null
at Main.main(Main.java:5)
对于更复杂的表达式:
map.get("key").process().getNestedValue();
增强的NPE消息会明确指出哪一部分是null:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Result.getNestedValue()" because the return value of
"ProcessedData.process()" is null
实用场景:调试复杂对象链、排查第三方库错误、缩短问题定位时间。
14. Switch表达式:更简洁的分支处理
JDK14正式发布了switch表达式(最初在JDK12引入为预览特性):
// 传统switch语句
String result;
DayOfWeek day = LocalDate.now().getDayOfWeek();
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
result = "Weekday";
break;
case SATURDAY:
case SUNDAY:
result = "Weekend";
break;
default:
result = "Invalid day";
break;
}
// 新的switch表达式
String result = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
default -> "Invalid day";
};
// 复杂表达式,带代码块
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> {
System.out.println("Six letters day");
yield 6;
}
case TUESDAY -> {
System.out.println("Seven letters day");
yield 7;
}
case THURSDAY, SATURDAY -> {
System.out.println("Eight letters day");
yield 8;
}
case WEDNESDAY -> {
System.out.println("Nine letters day");
yield 9;
}
default -> {
throw new IllegalStateException("Invalid day: " + day);
}
};
主要优点:
- 可以作为表达式返回值
- 箭头语法更简洁
- 使用逗号可以合并多个case
- 不需要break语句,消除了常见的错误源
- 穷尽性检查,确保所有情况都被处理
实用场景:状态机实现、命令处理、配置解析、业务逻辑分派。
15. 记录类(Records):数据类不再冗长
JDK14引入了Records作为预览特性,在JDK16正式发布,为不可变数据类提供了简洁的语法:
// 传统POJO类
public final class Employee {
private final String name;
private final int id;
private final Department department;
public Employee(String name, int id, Department department) {
this.name = name;
this.id = id;
this.department = department;
}
public String getName() { return name; }
public int getId() { return id; }
public Department getDepartment() { return department; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id &&
Objects.equals(name, employee.name) &&
Objects.equals(department, employee.department);
}
@Override
public int hashCode() {
return Objects.hash(name, id, department);
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + ''' +
", id=" + id +
", department=" + department +
'}';
}
}
// 使用Record
public record Employee(String name, int id, Department department) { }
Records自动生成构造器、访问器、equals/hashCode和toString方法。
你也可以向Record添加额外的构造器、方法和静态成员:
public record Point(int x, int y) {
// 自定义紧凑构造器
public Point {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Coordinates cannot be negative");
}
}
// 重载构造器
public Point() {
this(0, 0);
}
// 实例方法
public double distance(Point other) {
return Math.sqrt(Math.pow(this.x - other.x, 2) +
Math.pow(this.y - other.y, 2));
}
// 静态成员
public static final Point ORIGIN = new Point(0, 0);
// 静态方法
public static Point of(int x, int y) {
return new Point(x, y);
}
}
实用场景:DTO对象、API响应模型、消息体、不可变数据容器、值对象。
JDK 15-16 (2020年9月-2021年3月):文本和类型检查优化
16. 文本块:多行字符串不再痛苦
JDK15正式发布了文本块功能(在JDK13首次预览),让多行字符串变得简单优雅:
// 传统多行字符串
String json = "{\n" +
" "name": "John Doe",\n" +
" "age": 30,\n" +
" "address": {\n" +
" "street": "123 Main St",\n" +
" "city": "Anytown"\n" +
" }\n" +
"}";
// 使用文本块
String json = """
{
"name": "John Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
""";
// HTML示例
String html = """
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
""";
// SQL查询
String query = """
SELECT id, first_name, last_name
FROM employees
WHERE department_id = ?
ORDER BY last_name, first_name
""";
文本块还支持字符串插值和格式控制:
// 使用\避免行尾换行
String compact = """
<html>\
<body>\
<p>Hello</p>\
</body>\
</html>\
""";
// 与String::formatted配合使用
String template = """
Dear %s,
Your order #%d has been shipped on %s.
Thank you,
Customer Service
""";
String message = template.formatted("John", 12345, "2023-05-15");
实用场景:SQL查询、HTML/JSON/XML模板、代码生成、多行文本配置。
17. instanceof模式匹配:类型检查与转换合二为一
JDK16正式发布的instanceof模式匹配简化了类型检查和转换:
// 传统方式
if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
System.out.println(s.toUpperCase());
}
}
// 使用模式匹配
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
// 在复杂条件中使用
if (obj instanceof String s && s.length() > 10
|| obj instanceof List<?> list && list.size() > 5) {
// 使用s或list
}
// 与switch配合使用(JDK17预览特性)
Object value = getValue();
switch (value) {
case String s when s.length() > 5 -> System.out.println("Long string: " + s);
case String s -> System.out.println("Short string: " + s);
case List<?> l -> System.out.println("List with " + l.size() + " elements");
default -> System.out.println("Unknown type");
}
实用场景:多态对象处理、类型安全转换、空检查简化。
18. 外部内存访问API (Foreign Memory Access):安全高效的本地内存操作
JDK16引入了Foreign Memory Access API(孵化器阶段),为Java提供了安全高效的本地内存访问能力:
// 分配堆外内存
try (Arena arena = Arena.ofConfined()) {
// 分配100字节的本地内存
MemorySegment segment = arena.allocate(100);
// 写入数据
MemorySegment.copy(new byte[] {1, 2, 3, 4, 5}, 0, segment, 0, 5);
// 读取数据
byte value = segment.get(ValueLayout.JAVA_BYTE, 2); // 读取索引2的值
System.out.println("Value at index 2: " + value); // 输出 3
// 填充内存段
MemorySegment.fill(segment, (byte) 10);
// 使用VarHandle操作内存
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
intHandle.set(segment, 0, 42);
int result = (int) intHandle.get(segment, 0);
System.out.println("Integer value: " + result); // 输出 42
// 内存地址操作
long address = segment.address().toRawLongValue();
System.out.println("Memory address: 0x" + Long.toHexString(address));
}
这个API在JDK17中得到了改进,在JDK21中正式发布。它为需要处理大量数据的应用程序提供了比ByteBuffer更强大的替代方案。
实用场景:高性能计算、大数据处理、网络应用、与本地库集成。
JDK 17 (2021年9月):长期支持版的强大特性
19. 密封类(Sealed Classes):精确控制继承关系
JDK17正式发布的密封类允许更精确地控制哪些类可以继承一个类:
// 声明一个密封接口,只允许特定的类实现它
public sealed interface Shape
permits Circle, Rectangle, Triangle {
double area();
}
// 最终实现类,不允许进一步继承
public final class Circle implements Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
// 允许进一步继承的类
public non-sealed class Rectangle implements Shape {
private final double width;
private final double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
// 限制继承的类
public sealed class Triangle implements Shape
permits EquilateralTriangle, RightTriangle {
// ...实现...
}
// 允许的子类
public final class EquilateralTriangle extends Triangle {
// ...实现...
}
public final class RightTriangle extends Triangle {
// ...实现...
}
密封类与switch模式匹配和记录类结合使用时特别强大,编译器可以进行穷尽性检查:
double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> t.base() * t.height() / 2;
// 不需要default分支,因为编译器知道这些是Shape的所有可能实现
};
}
实用场景:领域模型设计、类型安全的API、状态机实现、编译器检查增强。
20. 增强的伪随机数生成器:更灵活、可预测的随机数
JDK17引入了增强的伪随机数生成器(PRNG)框架,提供了更多算法和更好的接口:
// 获取默认的随机数生成器
RandomGenerator random = RandomGenerator.getDefault();
System.out.println(random.nextInt(100)); // 0-99之间的随机数
// 使用特定算法的生成器
RandomGenerator xoroshiro = RandomGenerator.of("Xoroshiro128PlusPlus");
System.out.println(xoroshiro.nextLong());
// 使用L32X64MixRandom - 平衡了速度和质量的算法
RandomGenerator fastRandom = RandomGenerator.of("L32X64MixRandom");
for (int i = 0; i < 5; i++) {
System.out.println(fastRandom.nextInt(1000));
}
// 创建可复现的随机数序列 (使用相同的种子)
RandomGenerator seeded = RandomGenerator.of("Xoshiro256PlusPlus");
((SplittableRandomGenerator) seeded).setSeed(42);
for (int i = 0; i < 5; i++) {
System.out.println(seeded.nextInt(100));
}
// 生成随机流
DoubleStream randomDoubles = RandomGenerator.getDefault().doubles(1000);
randomDoubles.forEach(System.out::println);
// 查看所有可用的算法
RandomGenerator.all()
.map(provider -> provider.name() + ": " + provider.group())
.sorted()
.forEach(System.out::println);
实用场景:科学计算、模拟、游戏开发、测试数据生成、加密应用。
21. 向量API (Incubator):性能密集型计算
JDK17引入了孵化器阶段的向量API,支持SIMD(单指令多数据)风格的操作,显著加速特定类型的计算:
// 使用IntVector加速数组求和
static int sumArrayVectorized(int[] a) {
var species = IntVector.SPECIES_PREFERRED;
var sum = IntVector.zero(species);
var i = 0;
// 处理可以向量化的部分
for (; i <= a.length - species.length(); i += species.length()) {
var v = IntVector.fromArray(species, a, i);
sum = sum.add(v);
}
// 处理剩余元素
var result = sum.reduceLanes(VectorOperators.ADD);
for (; i < a.length; i++) {
result += a[i];
}
return result;
}
// 向量化矩阵乘法
static void multiplyMatricesVectorized(float[] a, float[] b, float[] c, int n) {
var species = FloatVector.SPECIES_PREFERRED;
int limit = species.length();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
var sum = FloatVector.zero(species);
int k = 0;
// 使用向量计算
for (; k <= n - limit; k += limit) {
var va = FloatVector.fromArray(species, a, i * n + k);
var vb = FloatVector.fromArray(species, b, k * n + j);
sum = sum.add(va.mul(vb));
}
// 累加向量结果
float dotProduct = sum.reduceLanes(VectorOperators.ADD);
// 处理剩余元素
for (; k < n; k++) {
dotProduct += a[i * n + k] * b[k * n + j];
}
c[i * n + j] = dotProduct;
}
}
}
JDK19和JDK21继续改进了向量API,但截至JDK21仍处于孵化器阶段。
实用场景:科学计算、图像处理、机器学习、信号处理、金融模拟。
JDK 18-19 (2022年3月-2022年9月):工具链和API增强
22. 简单Web服务器:快速启动静态文件服务
JDK18引入了一个简单的命令行HTTP服务器,可以快速启动静态文件服务:
// 命令行用法
// jwebserver -p 8000 -d /path/to/directory
// 在代码中使用:
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Path;
public class SimpleFileServer {
public static void main(String[] args) throws IOException {
var server = SimpleFileServer.createFileServer(
new InetSocketAddress(8000),
Path.of("/path/to/directory"),
SimpleFileServer.OutputLevel.VERBOSE
);
server.start();
System.out.println("Server started at http://localhost:8000");
}
}
这个功能特别适合开发和测试环境,比如快速预览静态网站或测试API调用。
实用场景:前端开发、静态网站预览、本地测试环境、原型开发。
23. 代码片段in JavaDoc:更好的API文档
JDK18引入了@snippet
标签,允许在JavaDoc中添加带有语法高亮的代码示例:
/**
* This class provides utility methods for string operations.
*
* <p>Example usage:</p>
* {@snippet :
* String result = StringUtils.capitalize("hello"); // Returns "Hello"
* boolean isEmpty = StringUtils.isBlank(" "); // Returns true
* }
*/
public class StringUtils {
// 类实现省略
}
增强的文档还支持高亮、区域标记和错误标记:
/**
* Example of snippet with highlighting:
* {@snippet :
* // @highlight region="important"
* String encoded = Base64.getEncoder().encodeToString(data);
* // @end
*
* // @highlight regex="data" type="bold"
* byte[] decoded = Base64.getDecoder().decode(encoded);
*
* // @replace regex="badPractice()" replacement="goodPractice()" type="error"
* result = badPractice();
* }
*/
实用场景:API文档、开源项目、技术指南、教程编写。
24. 外部函数接口 (Foreign Function & Memory API):Java调用本地代码更简单
JDK19改进了孵化器中的外部函数接口(在JDK21中正式发布),让Java与本地代码交互更加简单:
// 定义C库函数的接口
import java.lang.foreign.*;
import static java.lang.foreign.ValueLayout.*;
public class LibCDemo {
public static void main(String[] args) {
// 获取C标准库的链接器
Linker linker = Linker.nativeLinker();
// 查找printf函数
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle printf = stdlib.find("printf")
.map(addr -> linker.downcallHandle(
addr,
FunctionDescriptor.of(JAVA_INT, ADDRESS),
Linker.Option.firstVariadicArg(1)
))
.orElseThrow();
// 准备字符串参数
try (Arena arena = Arena.ofConfined()) {
MemorySegment cString = arena.allocateUtf8String("Hello from Java! Count: %d\n");
// 调用printf
try {
printf.invoke(cString, 42);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
这个API消除了JNI的大部分复杂性,提供了更安全、更简洁的方式来调用本地代码。
实用场景:与C/C++库集成、系统编程、性能关键应用、多语言项目。
JDK 20-21 (2023年3月-2023年9月):现代并发和语言增强
25. 虚拟线程(Virtual Threads):并发革命
JDK21正式发布了虚拟线程,这是Java并发编程的重大变革:
// 创建和启动单个虚拟线程
Thread.startVirtualThread(() -> {
System.out.println("Running in virtual thread");
});
// 使用虚拟线程运行多个任务
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交1000个任务,每个在独立的虚拟线程中运行
for (int i = 0; i < 1000; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " running on " + Thread.currentThread());
// 模拟IO操作
try {
Thread.sleep(Duration.ofMillis(100));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return taskId;
});
}
} // 自动关闭executor
// 以构建器方式创建虚拟线程
ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory();
Thread worker = factory.newThread(() -> {
// 任务代码
});
worker.start();
// 使用虚拟线程改写传统的阻塞式IO代码
void processFile(Path file) throws IOException {
// 这段代码在虚拟线程中运行时不会阻塞平台线程
try (var reader = Files.newBufferedReader(file)) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
}
}
虚拟线程的主要优势是可以创建数百万个轻量级线程,而不会耗尽系统资源。它们特别适合IO密集型应用,使同步代码可以获得与异步代码相当的扩展性。
实用场景:高并发Web服务器、微服务、数据处理管道、爬虫程序。
26. 结构化并发(Structured Concurrency):管理异步任务的生命周期
JDK21引入了结构化并发API(预览特性),简化了多线程代码的错误处理和资源管理:
// 并行获取用户及其订单
record User(int id, String name) {}
record Order(int id, double amount) {}
record UserWithOrders(User user, List<Order> orders) {}
UserWithOrders getUserWithOrders(int userId) {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行执行两个子任务
Future<User> userFuture = scope.fork(() -> fetchUser(userId));
Future<List<Order>> ordersFuture = scope.fork(() -> fetchOrders(userId));
// 等待所有任务完成
scope.join();
// 检查子任务是否有异常
scope.throwIfFailed(e -> new RuntimeException("Failed to fetch data", e));
// 获取结果
return new UserWithOrders(userFuture.resultNow(), ordersFuture.resultNow());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 使用结构化并发处理多个API调用
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var weatherFuture = scope.fork(() -> callWeatherAPI());
var trafficFuture = scope.fork(() -> callTrafficAPI());
var newsFuture = scope.fork(() -> callNewsAPI());
try {
scope.join();
scope.throwIfFailed();
// 所有API调用成功,处理结果
var dashboard = createDashboard(
weatherFuture.resultNow(),
trafficFuture.resultNow(),
newsFuture.resultNow()
);
return dashboard;
} catch (Exception e) {
// 有一个API调用失败,所有任务都被取消
return createFallbackDashboard();
}
}
结构化并发确保所有子任务在父任务退出前要么完成,要么被取消,避免了资源泄漏和"遗忘"的后台任务。
实用场景:API聚合、微服务通信、并行数据处理、复杂异步工作流。
27. Record模式(Record Patterns):解构数据更简单
JDK21正式发布的Record模式允许在模式匹配中解构记录:
// 定义一些记录
record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}
record Circle(Point center, int radius) {}
// 使用传统方式处理记录
Object shape = new Rectangle(new Point(1, 2), new Point(5, 6));
if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
Point topLeft = r.topLeft();
Point bottomRight = r.bottomRight();
int width = bottomRight.x() - topLeft.x();
int height = bottomRight.y() - topLeft.y();
System.out.println("Rectangle with width " + width + " and height " + height);
}
// 使用Record模式解构
if (shape instanceof Rectangle(Point(var x1, var y1), Point(var x2, var y2))) {
int width = x2 - x1;
int height = y2 - y1;
System.out.println("Rectangle with width " + width + " and height " + height);
}
// 结合switch使用
String getDescription(Object shape) {
return switch (shape) {
case Rectangle(Point(var x1, var y1), Point(var x2, var y2)) ->
"Rectangle from (%d,%d) to (%d,%d)".formatted(x1, y1, x2, y2);
case Circle(Point(var x, var y), var r) ->
"Circle at (%d,%d) with radius %d".formatted(x, y, r);
default -> "Unknown shape";
};
}
Record模式与嵌套模式结合使用时特别强大,可以轻松处理复杂的数据结构。
实用场景:数据转换、JSON/XML解析结果处理、领域模型操作、事件处理。
28. 字符串模板(String Templates):安全高效的字符串插值
JDK21引入了字符串模板作为预览特性,提供比字符串连接更简洁、更安全的方式:
// 传统方式
String name = "Alice";
int age = 30;
String message = "Hello, " + name + "! Next year, you'll be " + (age + 1) + ".";
// 使用字符串模板
String message = STR."Hello, {name}! Next year, you'll be {age + 1}.";
// 带表达式的模板
String status = "active";
String message = STR."User status: {status.toUpperCase()} (set {LocalDate.now()})";
// 格式化数字
double value = 1234.56789;
String formatted = STR."The value is {value%.2f}"; // "The value is 1234.57"
// 多行JSON
String json = STR."""
{
"name": "{name}",
"age": {age},
"isAdult": {age >= 18},
"contacts": [
{generateContactsJson()}
]
}
""";
字符串模板不仅语法简洁,还提供了类型安全和对表达式的编译时检查。
实用场景:生成JSON/XML/HTML、日志记录、消息格式化、SQL语句构建。
29. 序列集合 (Sequenced Collections):统一的集合操作
JDK21引入了SequencedCollection
、SequencedSet
和SequencedMap
接口,为Java集合框架添加了方向性和顺序性操作:
// 序列化集合基本用法
SequencedCollection<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
// 获取第一个和最后一个元素
String first = names.getFirst(); // "Alice"
String last = names.getLast(); // "Charlie"
// 添加元素到两端
names.addFirst("Zoe");
names.addLast("David");
System.out.println(names); // [Zoe, Alice, Bob, Charlie, David]
// 创建反向视图
SequencedCollection<String> reversed = names.reversed();
System.out.println(reversed); // [David, Charlie, Bob, Alice, Zoe]
// 序列化Map
SequencedMap<String, Integer> scores = new LinkedHashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 85);
scores.put("Charlie", 90);
// 获取第一个和最后一个条目
Map.Entry<String, Integer> firstEntry = scores.firstEntry(); // Alice=95
Map.Entry<String, Integer> lastEntry = scores.lastEntry(); // Charlie=90
// 添加条目到两端
scores.putFirst("Zoe", 100);
scores.putLast("David", 80);
// 获取键或值的序列化视图
SequencedCollection<String> keys = scores.sequencedKeySet();
SequencedCollection<Integer> values = scores.sequencedValues();
这些接口统一了Java集合框架中的顺序操作,使API更加一致。现有的有序集合类如ArrayList
、LinkedHashSet
和LinkedHashMap
都实现了这些新接口。
实用场景:维持插入顺序的集合、FIFO/LIFO队列操作、双向迭代、有序数据处理。
总结
Java语言在保持向后兼容性的同时,不断引入新特性以提高开发效率和代码质量。掌握这些新特性不仅能提高开发效率,还能编写出更加简洁、健壮的代码。