本文所写的这几个 Java 版本均为长期支持(LTS)版本。
1. Java 8
1.1. Lambda 表达式
Lambda 表达式允许你以一种更简洁的方式表示函数式接口的实例。它能够使代码更加简洁和易读。
java
// 示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
1.2. 函数式接口
函数式接口是只包含一个抽象方法的接口,可以使用 @FunctionalInterface 注解来标识。
java
@FunctionalInterface
interface MyFunctionalInterface {
void execute();
}
1.3. 方法引用
方法引用是一种更简洁地表示 lambda 表达式的方法。可以使用类名或对象名来引用方法。
java
// 示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);
1.4. Stream API
Stream API 提供了一种高效处理集合的方式,通过流式操作可以进行过滤、映射、排序等操作。
java
// 示例
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
1.5. Optional 类
Optional 类用于防止空指针异常,它可以包含值或不包含值。
java
// 示例
Optional<String> optionalName = Optional.ofNullable(null);
optionalName.ifPresent(System.out::println); // 不会输出任何内容
1.6. 默认方法
接口可以包含默认实现的方法,这样子类可以选择重写或者直接使用。
java
interface MyInterface {
default void defaultMethod() {
System.out.println("Hello from default method");
}
}
1.7. 新日期时间 API
Java 8 引入了新的日期时间 API 来替代旧的 java.util.Date 和 java.util.Calendar 类,提供更好的可读性和可用性。
java
// 示例
LocalDate today = LocalDate.now();
System.out.println(today);
1.8. 重复注解
Java 8 支持在同一位置上使用相同类型的注解多次。
java
@MyAnnotation
@MyAnnotation
public class MyClass {
}
2. Java 11
2.1. 局部变量类型推断
引入 var 关键字,编译器自动推断局部变量类型,简化代码书写。
java
var list = new ArrayList<String>(); // 类型自动推断为 ArrayList<String>
var name = "Java"; // 类型自动推断为 String
- 语法 :
var只能用于局部变量,编译器根据初始化值推断类型。 - 优势:减少样板代码,但需注意类型明确性。
- 限制:不能用于方法参数、返回值或类成员变量。
2.2. 字符串方法增强
Java 11 增加了一些新的字符串方法,改善了字符串操作的便利性。
- String.isBlank(): 判断字符串是否为空或仅包含空白字符。
- String.lines(): 将字符串按行分割并返回流。
- String.repeat(int count): 重复字符串指定次数。
java
String str = " ";
System.out.println(str.isBlank()); // true
String multiLine = "Hello\nWorld";
multiLine.lines().forEach(System.out::println); // 输出每一行
String repeated = "abc".repeat(3); // "abcabcabc"
2.3. 新 HTTP Client API
Java 11 引入了新的 HTTP 客户端 API,支持 HTTP/2,能够以非阻塞方式发送请求和接收响应。
java
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com"))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
2.4. 文件和目录的增强
Files 类中增加了 readString() 和 writeString() 方法,使得读取和写入文件更加方便。
java
Path path = Paths.get("example.txt");
Files.writeString(path, "Hello, World!"); // 写入文件
String content = Files.readString(path); // 读取文件
2.5. 弃用和删除不再使用的功能
Java 11 删除了一些过时的API和功能,例如:
- 移除 javax.xml.bind(JAXB)模块
- 移除 java.activation 模块
2.6. ZGC 垃圾收集器
Java 11 引入了实验性的 ZGC(零停顿垃圾回收器),旨在处理大内存应用程序,以提供更低的停顿时间。
可伸缩、低延迟垃圾收集器,停顿时间≤10ms,适用于大堆内存场景。
java
java -XX:+UseZGC MyApp // 启用 ZGC
- 核心特性:并发处理、堆内存压缩,停顿时间≤10ms(实际≤1.68ms)。
- 适用场景:高吞吐量、低延迟应用(如金融系统)。ZGC 适用于大堆内存场景,但需 Linux/x64 平台。
2.7. 运行时常量池
Java 11 优化了常量池的实现,以减少内存占用并提高性能。
2.8. 增强的 JEP
Java 11 包括多个 JEP,如:
JEP 318: Epsilon (无操作) 垃圾回收器 JEP 321: HTTP Client JEP 352: Non-Volatile Mapped Byte Buffers
无操作垃圾收集器,适用于嵌入式系统。
java
java -XX:+UseEpsilonGC MyApp // 启用 Epsilon
Java Flight Recorder(JFR),集成到 JVM,提供性能分析工具。
java
java -XX:+FlightRecorder MyApp // 启用 JFR
3. Java 17
3.1. 密封类(Sealed Classes)
通过 sealed 关键字限制类的继承范围,增强封装性。
密封类允许开发者控制哪些类可以扩展或实现某个类或接口。通过这种方式,可以定义一个受限的继承层次结构。
java
sealed class Shape permits Circle, Square {}
final class Circle extends Shape {}
final class Square extends Shape {}
3.2. 模式匹配(Pattern Matching for instanceof)
Java 17 引入了对 instanceof 的模式匹配,简化了类型检查和强制转换的过程。
java
Object obj = "Hello, World!";
if (obj instanceof String s) {
System.out.println(s.toUpperCase()); // 直接使用 s,不需要再次强制转换
}
3.3. 文本块(Text Blocks)
虽然在 Java 13 和 Java 14 中引入了文本块,Java 17 将其正式标准化。文本块使得多行字符串的书写变得更加简单和可读。
java
String json = """
{
"name": "Alice",
"age": 30
}
""";
3.4. 增强的随机数生成 API
Java 17 引入了一组全新的随机数生成器 API,提供更好的性能和灵活性,支持多种随机数生成策略。
java
RandomGenerator generator = RandomGenerator.of("L128X256MixRandom");
int randomInt = generator.nextInt(100); // 生成 0 到 99 之间的随机整数
3.5. JEP 411: java.nio.file.Path 的方法增强
新增的 Path 方法可以方便地从文件系统路径中获取文件名、父目录等信息。
java
Path path = Paths.get("/usr/local/bin/java");
System.out.println(path.getFileName()); // 输出 "java"
3.6. JEP 382: 新的 switch 表达式模式匹配
Java 17 对 switch 表达式进行了改进,简化了代码逻辑,使得处理多种情况更加直观。
java
String dayType = switch (day) {
case MONDAY, FRIDAY -> "Working Day";
case SATURDAY, SUNDAY -> "Weekend";
default -> "Unknown";
};
3.7. 外部函数和内存访问 API(JEP 412)
Java 17 提供了一种新的 API,用于与外部代码库进行交互,能够更容易、更安全地访问本地代码。
3.8. 移除旧的和不再使用的特性
Java 17 移除了多个较旧和不再使用的特性,例如移除了 Applet API 和其他过时的组件,以减少平台的复杂性。
3.9. 性能优化和安全性增强
Java 17 在性能和安全性方面进行了许多底层优化,以提高整体运行效率和安全性。
4. Java 21
4.1. 增强的模式匹配(Pattern Matching for switch)
Java 21 引入了对 switch 表达式的模式匹配,允许使用更灵活的条件来处理不同类型的对象。这使得可以在 switch 中简化类型检查和转换逻辑。
java
// 多值匹配
String unit = "cm";
String str = switch (unit) {
case "cm", "m" -> "厘米";
case "mm" -> "毫米";
case "km" -> "千米";
default -> "错误";
};
java
Object obj = "Hello, World!";
String result = switch (obj) {
case String s -> s.toUpperCase();
case Integer i -> "Integer: " + i;
default -> "Unknown type";
};
4.2. 记录类(Record Classes)增强
Java 21 对记录类进行了增强,允许在记录类中定义额外的方法、静态方法和实现接口。这使得记录类的功能更加丰富。
java
record Person(String name, int age) {
public String info() {
return name + " is " + age + " years old.";
}
}
4.3. 虚拟线程(Virtual Threads)
虚拟线程是 Java 21 的重要新特性,旨在显著简化并发编程,使得开发者能够轻松构建高并发应用程序。虚拟线程不再依赖于操作系统线程,而是通过 JVM 管理,从而减小了资源开销。
通过 M:N 调度模型(JVM 管理轻量级线程,映射到少量 OS 线程)实现高并发,单线程内存占用仅 400 字节(传统线程需 1MB+),支持百万级并发任务。
java
// 创建虚拟线程
Thread.startVirtualThread(() -> {
System.out.println("轻量级线程: " + Thread.currentThread());
});
// 虚拟线程池(生产推荐)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i ->
executor.submit(() -> processTask(i))
);
}
Thread.ofVirtual().start(() -> {
// 执行任务
});
4.4. 外部记忆 API(Foreign Function & Memory API)
这是一个实验性 API,提供了一种安全且高效地访问本地代码和内存的方式。它使得与 C 和其他语言编码交互变得更加容易和安全。
java
MemorySegment segment = MemorySegment.allocateNative(100);
segment.set(0, 42); // 设置数据
4.5. 增强的 JEP(JDK Enhancement Proposals)
Java 21 包含若干个重要的 JEP,例如:
- JEP 420: 列表类(List) --- 提供了更方便的操作。
- JEP 421: Scoped Values --- 一种新的 API,用于传递线程本地值。
4.6. JEP 431: String 方法的增强
Java 21 为 String 类增加了几个新方法,比如 String.stripIndent() 和 String.translateEscapes(),这些方法使字符串处理更加灵活。
java
String text = """
Hello,
World!
""".stripIndent(); // 可以去除缩进
4.7. Switch 表达式的增强
在 switch 表达式中,可以使用 yield 关键字返回结果,从而提高可读性。
java
int number = 3;
String description = switch (number) {
case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
default -> yield "Unknown Number";
};
4.8. 性能优化和安全性增强
Java 21 在底层实现上进行了多个性能优化和安全性增强,以提高运行时效率和减少潜在的安全漏洞。
5. Java 25
5.1. 实例main方法
不再需要public static void main(String[] args)的"仪式性"代码。现在,入门可以如此简单:
java
class HelloWorld {
void main() { // 注意:没有static,没有public
System.out.println("你好,Java 25!");
}
}
新手写第一个程序时,不用再被static、String[] args这些复杂概念吓到,学习曲线更平滑。
5.2. 灵活构造函数体
允许在调用父类构造器(super())之前执行一些逻辑,比如参数校验。
java
class PositiveInteger {
int value;
PositiveInteger(int val) {
if (val <= 0) throw new IllegalArgumentException("必须为正数");
super(); // 现在这行可以写在后面了
this.value = val;
}
}
以前构造函数的写法很死板,必须先调用super(),现在可以更自由地安排初始化逻辑的顺序,让代码更合理。
5.3. 原始类型模式匹配
在instanceof和switch中直接匹配int、double等基本类型。
java
Object obj = 42;
if (obj instanceof int i) { // 直接匹配int类型
System.out.println("这是一个整数: " + i);
}
处理未知类型的对象时,对于基本类型也能像对象一样进行优雅的类型判断和提取,代码更统一、安全。
5.4. 作用域值
用于在线程(特别是虚拟线程)内高效、安全地传递数据,旨在替代老旧的ThreadLocal。
java
final static ScopedValue<String> USER = ScopedValue.newInstance();
// 在某个作用域内绑定值
ScopedValue.where(USER, "张三").run(() -> {
System.out.println(USER.get()); // 输出:张三
// 在这里启动的虚拟线程也会自动继承这个值
});
虚拟线程的"轻量"也要求数据共享方式必须高效。作用域值就像是为虚拟线程量身定制的"任务传声筒",比传统方式更快、更不易出错。
5.5. 向量API
允许你编写能充分利用CPU SIMD指令的代码,大幅提升科学计算、AI模型推理等场景的性能。
java
// 将两个浮点数数组进行向量化加法(伪代码风格,实际使用需创建VectorSpecies)
float[] a = {1.0f, 2.0f, 3.0f, 4.0f};
float[] b = {5.0f, 6.0f, 7.0f, 8.0f};
float[] c = new float[4];
var species = FloatVector.SPECIES_256;
for (int i = 0; i < a.length; i += species.length()) {
var va = FloatVector.fromArray(species, a, i);
var vb = FloatVector.fromArray(species, b, i);
var vc = va.add(vb);
vc.intoArray(c, i);
}
// 结果c为:[6.0, 8.0, 10.0, 12.0],且CPU是并行计算的
以前Java做大量数据计算时有点"笨拙",一次只能算一个数。向量API让它能"一心多用",让CPU一次性处理一大块数据,特别适合图像处理、机器学习这些领域。
5.6. 密钥派生函数API
提供了生成加密密钥的标准方法。
java
// 使用HKDF算法从主密钥派生子密钥
SecretKey masterKey = ... // 主密钥
KDF kdf = KDF.of("HKDF-SHA256");
SecretKey derivedKey = kdf.deriveKey(masterKey, "会话密钥".getBytes(), 32);
加密开发更规范了。用一个根密钥安全地生成多个子密钥,就像用一把主钥匙配出几把不同用途的房门钥匙,是构建现代安全系统的基础。
5.7. JVM与底层:更省内存,更快启动
紧凑对象头:显著减少了64位系统上每个对象的内存开销。假设你的应用有数百万个小型对象(如订单项、DTO),这个优化可能直接带来10%以上的堆内存节省,并因更好的缓存利用率而提升速度。
分代Shenandoah垃圾回收器:低暂停时间的GC现在对年轻代和老年代采用不同优化策略,在保持"停顿时间短"优点的同时,提升了整体吞吐量和内存效率。对于需要高响应性的服务(如交易系统)是不错的选择。
提前方法分析与JFR增强:前者通过复用分析数据加速大型应用的启动;后者如方法剖析,能帮你精准定位到消耗CPU的具体方法,而不是仅仅停留在类层面,让性能调优事半功倍。