文章目录
java8被使用多年,但现在新的javaLTS版本不断更新,很多企业都开始了java的更新换代,从java11-java17-java21, 但是说实在的,到底有多少功能是我们小开发真正在意的呢,有多少更新是我们日常开发中真正可以被应用的呢,本文进行了一些总结,希望可以帮助大家快速升级java版本和掌握
Java8(LTS)
Lambda表达式(core)
在lambda之前我们使用的古老方式是 匿名内部类
List<String> list = Arrays.asList("A", "B", "C");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
lambda表达式完全简化了这一写法
List<String> list = Arrays.asList("A", "B", "C");
list.sort((o1, o2) -> o1.compareTo(o2));
lambda的写法实际并不是最重要的,重要的其实是背后的 流式操作(stream)
函数式接口 + 自定义函数传参
java8引入了大量的函数接口来提升代码的抽象能力,这里指的是用于构建通用工具,回调逻辑之类,同时将函数能够以参数的方式传递给另一个函数
//old way
public class Printer {
public void print(String msg) {
System.out.println(msg);
}
}
Printer printer = new Printer();
printer.print("Hello");
//new way(combine lambda)
Consumer<String> printer = msg -> System.out.println(msg);
printer.accept("Hello");
Stream API
Stream API又称为声明式数据处理,
stream对于处理list在日常的使用中,让我们减少了非常多的重复for循环
stream 的设计初衷就是为了支持声明式、链式、惰性求值的数据处理方式
//before java8
List<String> list = Arrays.asList("a", "bb", "ccc");
List<String> filtered = new ArrayList<>();
for (String s : list) {
if (s.length() > 1) {
filtered.add(s.toUpperCase());
}
}
//java8 链式调用,可读性更高,便于组合对于list的复杂操作
List<String> result = list.stream()
.filter(s -> s.length() > 1)
.map(String::toUpperCase)
.collect(Collectors.toList());
Optional类
optional类的存在是为了防止代码中频繁的NullPointerException,也就是说在使用Optional类后,即使是null值也可以正常运行程序,不抛错出来
//before java8, 所有null值都要记得手动判断
public String getName(User user) {
if (user != null && user.getName() != null) {
return user.getName();
}
return "unknown";
}
//java8
public String getName(User user) {
if (user != null && user.getName() != null) {
return user.getName();
}
return "unknown";
}
default method
接口默认方法, Java8为了支持函数式编程和流式处理,对核心接口如
Iterable,Collection,List,Map等增加了很多新的方法
但是如果没有默认方法机制,这些接口的新增方法将导致旧代码全部崩溃,JDK 自己都编译不过去。所以,为了在不破坏已有代码的前提下演进接口设计,Java 8 引入了
default
//before java8, 接口与无法有实现,必须新建抽象类来提供默认实现
//java8
interface Hello {
default void sayHi() {
System.out.println("Hi");
}
}
新的时间/日期 API(java.time)
java8以前我们用calendar和date进行时间操作,但是这两个类并非是线程安全的,所以java8引入了LocalDate
//before java8
Calendar cal = Calendar.getInstance();
cal.set(2023, Calendar.JULY, 1);
Date date = cal.getTime();
//java8
LocalDate date = LocalDate.of(2023, 7, 1);
- 不可变对象 天然线程安全,多个线程访问同一个
LocalDate实例不会发生并发冲突。 - 所有对
LocalDate的操作(比如plusDays()、minusWeeks())都会返回一个新对象,而不修改原对象。这样就不会担心数据被意外改动 - 还是函数式编程,
LocalDate的不可变性让它天然适合在函数式链式操作中使用,说人话就是在整个数据操作过程中,不会破坏任何数据
不会。LocalDate 是轻量级的 final 类,且广泛使用了 对象复用、缓存 和 逃逸分析优化(JVM 可自动将短生命周期对象分配在栈上),性能非常优秀,远比 Date/Calendar 高效、安全。
方法引用(::)
::并不只是单纯的两个冒号,它有一个新的名称,叫做方法引用符,实际上是lambda表达式的语法糖,存在的主要意义是极简语法,提高可读性,与lambda打配合,使代码更优雅
list.forEach(System.out::println); // 等价于 s -> System.out.println(s)
重复注解(@Repeatable)
实际上在日常开发中不是很常用,但是嗯~,还是有必要知道的
//以前一个注解只能使用一次,现在可以多次重复使用,便于元数据编程(又是很大的一部分了,不在此赘述)
//java8 now
@Hint("hint1")
@Hint("hint2")
class Person {}
异步编程增强CompletableFuture
CompletableFuture是对Future的增强,提供了更强的异步能力、链式调用、组合任务等现代异步编程所需的一切能力
//传统Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Hello";
});
String result = future.get(); // 阻塞
System.out.println(result);
//存在的问题
❌ 只能阻塞等待结果 必须调用 get() 才能拿到结果,而且是阻塞的
❌ 无法主动完成 只能由线程池完成任务,不能外部 setResult
❌ 不支持任务组合 想要两个 Future 组合结果非常麻烦
❌ 不支持异步回调 没有 onComplete、thenApply 等回调
❌ 不支持异常处理链 错误处理不方便,必须 try-catch
❌ 不支持流式编程 不能链式调用、组合任务等
//非阻塞,异步回调
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println); // 输出 Hello World
//多任务组合
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<String> combined = future1.thenCombine(future2, (a, b) -> a + b);
System.out.println(combined.get()); // 输出 AB
//优雅支持异常处理链
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("boom");
return "ok";
}).exceptionally(ex -> "error: " + ex.getMessage())
.thenAccept(System.out::println); // 输出 error: boom
//支持手动完成,超时控制等高级功能
CompletableFuture<String> future = new CompletableFuture<>();
// 可用于回调模式中手动完成
future.complete("manually set");
Java9
JPMS
关于这个,暂时仅谈论概念层面,不在此过多阐述
JPMS是将JDK变成模块化,支持更细粒度的依赖管理和封装,非常适合大型项目
不可变集合的快速创建
这个东西非常实用,尤其是在进行测试的时候,我们不用再从new开始为list数据赋值
而且简洁,线程安全,避免了Arrays.asList返回固定大小可变集合的坑
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("x", 10, "y", 20);
//注意,数据大小超过10个以上会有问题
Stream Api增强
新增了一些方法,例如takeWhile, dropWhile,包括iterate新增了带条件的重载,更为灵活
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> taken = numbers.stream()
.takeWhile(n -> n < 4) // 从头开始取,直到条件不满足
.collect(Collectors.toList()); // 结果 [1, 2, 3]
List<Integer> dropped = numbers.stream()
.dropWhile(n -> n < 4) // 从头开始跳过,直到条件不满足
.collect(Collectors.toList()); // 结果 [4, 5, 6]
Optional增强
增加了ifPresentOrElse, 避免了手动写if(opt.isPresent)
Optional<String> opt = Optional.of("Hello");
opt.ifPresentOrElse(
s -> System.out.println("Value: " + s),
() -> System.out.println("No value")
);
CompletableFuture 增强
增加异步处理超时
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> "task")
.completeOnTimeout("timeout", 1, TimeUnit.SECONDS);
cf.thenAccept(System.out::println);
Java10
新特性速览
| 特性类别 | 具体内容 | 是否日常可用 | 用途说明 |
|---|---|---|---|
| ✅ 局部变量类型推断 | var 关键字:局部变量自动推断类型 | ✅ 强烈推荐 | 简化代码,提升可读性与开发效率 |
| JVM 性能优化 | GC 改进(如 G1 默认使用并行 full GC) | ❌ 底层 | 提升大堆场景的 GC 性能 |
| 容器环境支持 | JVM 能正确识别容器的 CPU 和内存限制(如 Docker 限制) | ✅ DevOps 场景 | 更准确的资源控制,适合容器部署 |
| 应用类数据共享(AppCDS) | AppCDS(Application Class-Data Sharing)支持所有类 | ✅ 部署优化 | 减少冷启动时间,尤其适合大型项目 |
| GC 接口标准化 | 将 GC 接口标准化,方便引入新的垃圾回收器 | ❌ 底层 | 为后续 GC 创新奠定基础 |
| root-certificate 集成 | 开箱即用支持默认 CA 根证书列表 | ✅ 安全相关 | 不再手动配置 SSL 证书(适用于安全连接) |
Var 局部类型推断
java是强类型语言,var的存在并不是将java从强类型转换为若类型语言的,主要使用场景如下:
- 泛型声明,避免重复书写类型
- 遍历中间变量
- 临时变量,例如stream的中间变量,作为结果的缓存(非常常用)
//java会根据右侧推测类型
var list = new ArrayList<String>();
var map = new HashMap<String, Integer>();
var name = "Dean";
var count = 42;
//作为临时变量来处理stream
public class VarInStreamExample {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob", "Charlie", "David");
List<String> result = names.stream()
.map(name -> {
var upper = name.toUpperCase(); // var 用于临时变量
var formatted = "[" + upper + "]"; // 再处理
return formatted;
})
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
//当然也可以在filter中用
List<String> filtered = names.stream()
.filter(name -> {
var length = name.length(); // 使用 var 作为中间值
return length > 3;
})
.collect(Collectors.toList());
filtered.forEach(System.out::println);
//但是不能在lambda的参数中使用(JDK 10 不支持,但 JDK 11+ 支持 var name ->)
names.stream().map((var name) -> name.toUpperCase()); // ❌
最重要的是,var因为是从右侧推测类型的,所以必须立即初始化,不要写未初始化的var 变量
Java 11 (LTS)
新特性速览
| 特性类别 | 特性简介 | 是否日常可用 | 实用价值 |
|---|---|---|---|
| ✅ 语法增强 | var 可用于 Lambda 参数 | ✅是 | 简化Lambda参数声明,支持加注解 |
| ✅ 标准 API 增强 | 字符串、文件、集合新方法 | ✅ 是 | 更加高效优雅处理字符串,文件,集合 |
| ✅ HTTP Client | 新的 HttpClient 替代 HttpURLConnection,支持异步和 HTTP/2 | ✅ 是 | 发起 HTTP 请求更方便、功能更全 |
| ✅ 单文件运行 | java Hello.java 直接运行 | ✅ 小工具开发、脚本场景 | 编写/调试临时脚本,快速验证 Java 代码段 |
| ✅ 本地接口调用(Nestmates) | 支持类之间访问私有成员 | ⭕ JVM 底层优化 | JVM 优化嵌套类访问逻辑,语法级变化小 |
| ✅ GC 改进 | 支持 ZGC、Epsilon GC | ⭕ 特殊性能调优时用 | 提高吞吐量或最小暂停时可选,部署调优时用 |
| ❌ 模块移除 | 移除了 Java EE 和 CORBA 模块 | ⚠️ 如果用到旧 API 需额外依赖 | 使用 Java EE API 时需引入第三方依赖 |
| ✅ Flight Recorder & Mission Control | Java Flight Recorder(JFR)与 Mission Control 开源并集成 | ⭕ 性能调优分析工具 | 性能调试与运行分析更强大,替代 commercial 工具 |
| ✅ 异常处理改进 | 新增 java.lang.invoke.ConstantDesc 和 var 在 try-with-resources 中支持 | ❌ 很少用 | 多用于库/框架开发或编译器支持 |
String增强
Java11对于字符串的处理上,提供了更多的API
var text = " \n hello \n";
// 去除空白行
System.out.println(text.isBlank()); // true(全是空白)
// 去除首尾空白字符
System.out.println(text.strip()); // "hello"
// 去除前导空白
System.out.println(text.stripLeading()); // "hello \n"
// 去除尾部空白
System.out.println(text.stripTrailing()); // " \n hello"
// 行切割
"abc\ndef\nghi".lines().forEach(System.out::println); // 输出3行
// 重复字符串
System.out.println("ha".repeat(3)); // "hahaha"
文件操作增强
Files.readString()/writeString()简化了文件读写,不仅语义更加简化直观,而且减少了中间类型,编码处理,降低的文件处理的错误率
//java8 read
Path path = Paths.get("hello.txt");
String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
//java11 read
Path path = Paths.get("hello.txt");
String content = Files.readString(path);
//java8 write
Path path = Paths.get("output.txt");
Files.write(path, "Hello".getBytes(StandardCharsets.UTF_8));
//java11 write
Path path = Paths.get("output.txt");
Files.writeString(path, "Hello");
HttpClient
HttpCient作为HttpURLConnection的替代品出现,不仅支持同步异步,还内建支持HTTP/1.1 和HTTP2, 从此不再需要第三方库也能优雅发送请求,同时还支持连接池,超时控制等
//java8 send a request
URL url = new URL("https://httpbin.org/get");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
//java 11
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Var增强
java10引入了var,java11对于var进行了升级,使之支持Lambda参数,可用配合注解使用
//java8 lambda表达式的参数声明
list.forEach((String s) -> System.out.println(s));
//java11
list.forEach((@Nonnull var s) -> System.out.println(s)); //支持注解
单文件java源码运行
Java 11 引入了 单文件 Java 源码直接运行功能,可以像脚本语言一样使用
这在使用时感受并不是很强烈,但是这一个特性的存在让Java变得更为轻量,快速,上手门槛进一步降低,可以像python一样,直接写一些小工具,学习脚本,临时测试等东西,实际是非常大的提升
在过去,我们如果想要运行一个java文件,需要先编译后运行
javac Hello.java
java Hello
//但对比同期的其他语言,真是输在起跑线上
python hello.py
node hello.js
go run hello.go
//因此java11为了降低这个起步成本,缩小与其他语言的差异,引入了单文件运行
java Hello.java // 省略编译
实际上在Java9的时候,就有这个势头了,java9引入了 jshell,但 JShell 更适合交互式试验,不适合写一段完整脚本。而单文件运行是需要同时满足以下条件的:
- 写一个文件就能运行 ✅
- 支持
main方法和完整类结构 ✅ - 适合自动化小脚本、CI 工具等场景 ✅
模块移除
java11 移除了Java EE相关的一些重要模块,但这实际上也增加了大家从java8升级到java11的成本,如果你在java8使用这些模块,那么升级后java11中需要自行添加三方依赖,不仅有可能存在着版本兼容的问题,还可能提高安全风险
| 移除模块 | 包含内容 |
|---|---|
java.xml.bind | JAXB |
java.activation | JavaBeans Activation |
java.xml.ws | JAX-WS(SOAP) |
java.corba | CORBA |
Java 12 & Java 13
Java12和Java13 都不是LTS的版本,相对于Java11来说,更新也不是很多,且多为语言层面的小幅增强或JVM内部改进,而且是临时试用特性,并不推荐正式部署,所以不用花太多时间在上面
增强版Switch
实际是预览特性,java14才成为标准,所以在java12需要通过命令行开启
java --enable-preview --source 12 MyClass.java
//java8 写法
String dayType;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
dayType = "Working Day";
break;
case SATURDAY:
case SUNDAY:
dayType = "Weekend";
break;
default:
throw new IllegalArgumentException("Invalid day");
}
//java12 简洁了巨多
String dayType = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
case SATURDAY, SUNDAY -> "Weekend";
default -> throw new IllegalArgumentException("Invalid day");
};
解释一下为什么不再需要break了,在 Java 12 中,
switch被设计为一种 表达式(而不仅仅是语句),每个分支使用->,只能返回一个值,不允许 fall-through(穿透),因此break就不再需要了,也没有意义了。另外如果遇到复杂情况,例如每个分支需要多语句是,
可以使用yield
Collectors.teeing()
可以合并两个流的结果,适合场景就是 可以同时计算 平均值和总和,最大值和最小值等双结果
//java8 写两次
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream().mapToInt(i -> i).sum();
double avg = numbers.stream().mapToInt(i -> i).average().orElse(0);
//java12
var result = numbers.stream().collect(
Collectors.teeing(
Collectors.summingInt(i -> i),
Collectors.averagingInt(i -> i),
(sum, avg) -> Map.of("sum", sum, "avg", avg)
)
);
System.out.println(result); // {sum=15, avg=3.0}
CompactNumberFormat紧凑数字格式
为UI而生,生成可读性更强的数据
NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
System.out.println(shortFormat.format(1_000_000)); // 1M
Text Blocks 文本块
这是Java13中的预览特性,目的是为了解决Java多行字符串写起来太烦的问题,因为是预览特性,所以也要加编译参数
javac --enable-preview --release 13 Hello.java
java --enable-preview Hello
Sql
//java13之前
String sql = "SELECT id, name, age FROM users\n" +
"WHERE age > 18\n" +
"ORDER BY name;";
//java13, 对于sql无需再使用额外的换行符和转义符,缩进也更友好
String sql = """
SELECT id, name, age FROM users
WHERE age > 18
ORDER BY name;
""";
Json
//java13之前
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30\n" +
"}";
//java13
String json = """
{
"name": "Alice",
"age": 30
}
""";
除此之外,对于写HTML模板,构造内联Shell脚本,正则模板,配置模板都更简单,以下是Text Bolck的主要特点和语法说明
| 特性 | 说明 |
|---|---|
使用 """ 包裹 | 开始于行尾的 """,结束于独立的一行 """ |
| 自动换行 | 每行都自动加 \n(无需写 \n) |
| 自动缩进对齐 | 会根据最小缩进行统一“左移” |
可以使用 \n \t | 支持传统转义字符 |
可写 \" | 不必像以前一样转义双引号 |
Java14
Java14是2020年发布,依然是一个短期脚本,但是它带来了一些非常关键的语言特性预览,例如java12中提到的switch表达式从预览转为正式语法,添加了record数据类,做了NPE的优化等
新特性速览
| 特性类别 | 具体内容 | 是否日常可用 | 用途说明 |
|---|---|---|---|
| ✅ 语法增强 | switch 表达式(标准化) | ✅ 是 | 从 Java 12 预览转为正式语法,写法现代且更安全简洁 |
| ✅ 新语法预览 | record 数据类(预览) | ✅ 是(开启预览) | 简洁定义只用于保存数据的类 |
| ✅ NPE 优化 | 更清晰的 NullPointerException 信息 | ✅ 是 | 报错信息直接告诉你是哪个变量是 null |
| ✅ 工具增强 | jpackage(打包工具) | ✅ 是 | 可直接打包为原生可执行文件(Windows、Mac、Linux) |
| ⭕ 语法预览 | instanceof 模式匹配(预览) | ⭕ 可开启 | 类型判断 + 自动类型转换,减少样板代码 |
| ⭕ GC 改进 | ZGC、G1 等性能优化 | ❌ 少用 | 大型系统调优用 |
| ⭕ JFR 持续事件 | Flight Recorder 支持长期运行的数据采集 | ❌ 少用 | 性能分析用 |
Record
switch在java12已经对比过,因此不再赘述,Record类诞生是为了承载数据的类(DTO),自动生成
构造器 + getter + equals + hashCode + toString
Record的存在使之更适合作为 POJO、DTO、VO、响应体,怎么说呢,为Web而生吧
//java14之前
public class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override public String toString() { return "User{name=" + name + ", age=" + age + "}"; }
// 还要写 equals, hashCode...
}
//java14
public record User(String name, int age) {}
NPE更清晰
自Java14开始,当你遇到空指针时,报错会告诉你哪一个变量是null,而不再是模糊的NullPointerException,对于debug非常友好
//java14之前
Exception in thread "main" java.lang.NullPointerException
at MyApp.main(MyApp.java:6)
//java14
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Person.getName()" because "person" is null
instanceof 模式匹配(预览)
Java 14 开始支持以下语法(Java 16 成为正式语法),模式匹配会让代码更安全(编译器保证类型)
//java14之前,需要手动强制转换
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
//java14,自动转
if (obj instanceof String s) {
System.out.println(s.length());
}
Java15
Java15主要也是针对语言特性方面的更新,其次就是对于JVM的垃圾收集器进行了一些更新,除此之外弃用并移除了部分安全算法
Text Blocks(多行字符串)
在Java13的时候进行了测试版本的使用,在Java15的时候正式标准化
//java14及以前(大量的+和\n)
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 25\n" +
"}";
System.out.println(json);
//java15
String json = """
{
"name": "Alice",
"age": 25
}
""";
System.out.println(json);
CharSequence.isEmpty()
使之与 String.isEmpty() 保持一致
//java15前
CharSequence cs = "hello";
if (cs.length() == 0) {
System.out.println("Empty");
}
//java15
CharSequence cs = "hello";
if (cs.isEmpty()) {
System.out.println("Empty");
}
Scaled Classes(封闭类)
限制了哪些类可以继承某个类,以手动管理类权限,提升安全性和可维护性
//java15以前,无法直接限制
abstract class Shape {}
class Circle extends Shape {}
class Square extends Shape {}
// 其他包也能随便继承 Shape
//java15
public sealed class Shape permits Circle, Square {}
final class Circle extends Shape {}
final class Square extends Shape {}
// 其他类无法继承 Shape
Java16
Java16是2021年发布的短期版本(Non-LTS),生命周期仅有6个月,主要延续了java14和java15的语言改进方向
instanceof 模式匹配(Pattern Matching)
该模式从java14开始预览,在java16中正式发布,从此避免了在instanceof 后还要进行强制类型转换
//java15之前
Object obj = "Hello Java";
if (obj instanceof String) {
String s = (String) obj; // 必须手动强制类型转换
System.out.println(s.toUpperCase());
}
//java16
Object obj = "Hello Java";
if (obj instanceof String s) { // 直接定义变量 s
System.out.println(s.toUpperCase());
}
Record类型正式版
也是一样java14预览,java16正式发布
//java15及以前
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override
public String toString() { return name + " (" + age + ")"; }
}
//java16
public record Person(String name, int age) { }
- 自动生成构造器、
toString()、equals()、hashCode() - 不可变性更安全
- 写法极度简洁
Stream.toList()
//java15及以前
List<String> list = Stream.of("A", "B", "C")
.collect(Collectors.toList());
//java16
List<String> list = Stream.of("A", "B", "C").toList();
Java17(LTS)
Java17是继Java11之后的一个TLS版本,Java15和java16中以预览出现的一些功能,在Java17中正式发布,例如我们提到的Sealed Class,switch的新写法,Stream.toList()等,同时废除了一些几乎不再被使用的特性(Applets,RMI Activation 系统(过时的分布式机制),Security Manager(计划未来移除))
因为之前以及提到过Sealed Class, switch的pattern格式,Stream.toList的更新用法,所以在此不再重复赘述
增强随机数API
//java16及以前
Random random = new Random();
int num = random.nextInt(10);
//java17
RandomGenerator generator = RandomGenerator.of("L64X128MixRandom");
int num = generator.nextInt(10);
优点是
- 可选择不同算法(如更均匀分布、更高性能的生成器)
- 支持流式生成随机数
Java18
Java18 是2022年发布的,生命周期仅6个月的短期支持版本, 核心在java17的基础上做了小幅的语言改进,JDK的内部优化,以及一些实验性的API
SimpleWebServer
//java17及以前,启动一个HTTP服务器需要写很多代码或引入第三方包
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
public class OldHttpServer {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/", exchange -> {
String resp = "Hello World";
exchange.sendResponseHeaders(200, resp.length());
exchange.getResponseBody().write(resp.getBytes());
exchange.close();
});
server.start();
}
}
//java18
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.nio.file.*;
public class SimpleWebServerExample {
public static void main(String[] args) throws Exception {
var server = HttpServer.create(new InetSocketAddress(8000), 0);
var handler = com.sun.net.httpserver.SimpleFileServer.createFileHandler(Path.of("."));
server.createContext("/", handler);
server.start();
System.out.println("Server started at http://localhost:8000/");
}
}
//启动后访问 http://localhost:8000/ 就能看到当前目录的文件
SimpleWebServer存在的意义并不是取代Java web(做网站),而是快速提供HTTP访问(例如
适合做 本地调试前端页面、快速共享文件、演示 HTML/CSS/JS)
-
在Java18之前,如果你想通过HTTP提供一个目录下的文件,有几个选择
- Tomcat/Jetty——过于兴师动众
- 一个
HttpServer程序——多行代码实现 - Python/Node.js内置HTTP服务——非Java原生
-
那么Java18添加了Simple Web Server后,相当于自己内置了一个HTTP服务(零代码,零依赖,跨平台一致)
本质
- 它就是一个超轻量级 HTTP 文件服务器
- 没有 JSP、没有 Servlet、没有业务逻辑
- 替代的不是 Tomcat,而是
python -m http.server这类命令
UTF-8作为默认字符集
//java17及以前
// 在不同系统上,默认编码可能是 UTF-8(Linux)或 GBK(Windows)
System.out.println(Charset.defaultCharset()); // Windows 上可能输出 GBK
//java18
System.out.println(Charset.defaultCharset()); // 永远是 UTF-8,彻底消除跨平台编码差异
String API增强
//java17及以前
String str = "hello";
String upper = str.toUpperCase();
String indented = "line1\nline2".replace("\n", "\n "); // 手动缩进
//java18
String result = "hello".transform(s -> s.toUpperCase()); // HELLO
String indented = "line1\nline2".indent(4); // 自动缩进
String escaped = "Hello\\nWorld".translateEscapes(); // 解析为换行
@snippet(JavaDoc 代码块)
//java17及以前
/**
* 例子:
* <pre>
* System.out.println("Hello");
* </pre>
*/
//java18
/**
* 演示输出:
* {@snippet :
* System.out.println("Hello");
* }
*/
代码可编译,可被 JavaDoc 工具直接验证
Java19
2022年9月推出的短期版本,为Java21(LTS)的出现做铺垫
Record Pattern
// Java 18 及之前
record Point(int x, int y) {}
Object obj = new Point(1, 2);
if (obj instanceof Point) {
Point p = (Point) obj;
int x = p.x();
int y = p.y();
System.out.println(x + ", " + y);
}
//java19更简洁
record Point(int x, int y) {}
Object obj = new Point(1, 2);
if (obj instanceof Point(int x, int y)) {
System.out.println(x + ", " + y);
}
更好的配合 instanceof 和 switch 使用,可以直接解构 record 的字段。
Switch Pattern的进一步改进
//java18及以前
static String formatter(Object obj) {
if (obj instanceof Integer i) {
return "int " + i;
} else if (obj instanceof String s) {
return "string " + s;
}
return "unknown";
}
//java19
static String formatter(Object obj) {
return switch (obj) {
case String s && s.length() > 5 -> "Long string: " + s;
case String s -> "Short string: " + s;
case null -> "Null value";
default -> obj.toString();
};
}
可以看到其支持了更复杂的模式和null 匹配
Virtual Thread(虚拟线程)
- Project Loom 的核心功能:虚拟线程,极大提升并发能力。
- 虚拟线程是由 JVM 管理的轻量线程,可以创建百万级别的线程而不占用大量内存,其出现的意义是简化高并发编程,替代复杂的异步回调
//java18及以前,我们使用传统线程池,假设有1000 个任务需要大量系统线程,会占大量内存
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
Thread.sleep(1000);
System.out.println(Thread.currentThread());
});
}
executor.shutdown();
//java19 虚拟线程,每个任务一个虚拟线程,调度极轻量
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
Thread.sleep(1000);
System.out.println(Thread.currentThread());
});
}
executor.shutdown();
结构化并发
结构化并发是一种管理并发任务的 API,把多个任务组织成一个“作用域”,方便取消、等待和错误处理,该功能在java19处于孵化阶段,但结构化并发的出现对于管理复杂任务非常方便
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> findUser());
Future<Integer> order = scope.fork(() -> fetchOrderCount());
scope.join(); // 等待所有
scope.throwIfFailed(); // 如果任何失败则抛异常
System.out.println(user.resultNow() + " - " + order.resultNow());
}
Java20
Java20是2023年3月发布的短期版本,该版本的主要作用依然是继续推动Project Loom(虚拟线程,结构化并发),模式匹配等特性的演进
Record Pattern V2
//java19及以前
record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}
static void print(Rectangle r) {
if (r instanceof Rectangle(Point tl, Point br)) {
System.out.println("TopLeft = " + tl + ", BottomRight = " + br);
}
}
//java20,无须层层拆解
record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}
static void print(Rectangle r) {
if (r instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
System.out.println("From (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
}
}
Switch模式匹配V4
之前不是改成了可以直接用lamada表达式嘛,但现在开启了模式守卫(guard),这个模式可以让你直接在case 中加条件
//java19及之前
static String format(Object obj) {
return switch (obj) {
case Integer i -> "int " + i;
case String s -> "string " + s;
default -> "unknown";
};
}
//java20,有点像Kotlin/Scala这种现代模式匹配语言
static String format(Object obj) {
return switch (obj) {
case Integer i when i > 0 -> "positive int " + i;
case Integer i -> "non-positive int " + i;
case String s -> "string " + s;
default -> "unknown";
};
}
虚拟线程V2
主要是bug修复和行为细节改进,代码用法上和Java19基本相同
对了,这个东西我目前不建议使用,因为新开发的功能,似乎看起来不是非常稳定
//用法一致
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
Thread.sleep(100);
System.out.println(Thread.currentThread());
return null;
});
}
}
java20的变化主要是
- 内部调度更稳定,不容易死锁
- 与
ThreadLocal更好兼容 - 引入了 Scoped Values(JEP 429),作为
ThreadLocal的替代方案
Java21(LTS)
java21是2023年9月发布的LTS版本,是Java17后的下一个长期支持版本,也是很多企业目前从java8升级的目标版本,Java21里对我们之前提到的很多孵化器和预览特性功能进行了正式落地
主要我们日常可以使用的还是之前提到的Switch模式匹配,Record模式,虚拟线程这些用法之前已经做了详细的对比和说明,不再赘述
说一下前面未提到的功能
字符串模板
这个真的是非常常用的特性,可以像Kotlin/JS一样的字符串插值
//java20及之前
String name = "Alice";
int age = 20;
String msg = String.format("Name: %s, Age: %d", name, age);
//java21
String name = "Alice";
int age = 20;
String msg = STR."Name: \{name}, Age: \{age}";
System.out.println(msg); // Name: Alice, Age: 20
还支持 SQL / JSON 模板,防止拼接注入问题
//java20之前
// 用户输入
String user = "Alice";
String pwd = "' OR '1'='1";
// 危险的拼接写法
String sql = "SELECT * FROM users WHERE name = '" + user + "' AND password = '" + pwd + "'";
System.out.println(sql);
// SELECT * FROM users WHERE name = 'Alice' AND password = '' OR '1'='1'
//java21
//SQL模板
import static java.lang.StringTemplate.RAW;
String user = "Alice";
String pwd = "' OR '1'='1";
// 假设有一个 SQL 模板处理器(通常由第三方库或 JDBC 驱动提供)
SQLProcessor SQL = new SQLProcessor();
StringTemplate query = RAW."SELECT * FROM users WHERE name = \{user} AND password = \{pwd}";
PreparedStatement ps = connection.prepareStatement(SQL.process(query));
// SQLProcessor 会自动把 \{user}, \{pwd} 转换为参数绑定(? 占位符)
// 例如:SELECT * FROM users WHERE name = ? AND password = ?
// 然后安全地 set 参数
//好处是 自动参数化,不需要自己 ps.setString(),直接规避 SQL 注入。
//json模板
import static java.lang.StringTemplate.STR;
String name = "Alice";
int age = 25;
// 通过 JSON 处理器自动生成合法 JSON
JsonTemplateProcessor JSON = new JsonTemplateProcessor();
StringTemplate jsonTpl = STR."""
{
"name": "\{name}",
"age": \{age}
}
""";
String json = JSON.process(jsonTpl);
System.out.println(json);
// {"name":"Alice","age":25}
无名模式 & 无名变量
这个模式可以减少“用不到的变量名”
//java20及以前
if (obj instanceof String s) {
System.out.println("Got a string");
}
//java21
if (obj instanceof String _) {
System.out.println("Got a string");
}
//_ 表示“占位符”,编译器知道类型,但你不需要变量。

800

被折叠的 条评论
为什么被折叠?



