JDK 的新特性 (9 ~ 22) : 开发者的体感版本

学点新东西总是不太亏的。

JDK9 Features

模块化 Modular System

代码参考 hello-module 与博文 Java SE 9 模块化示例

出现的背景是,当我们依赖的 jar 的时候,我们是依赖了这个 Jar 的所有内容,jar 里面只有能 public private 来控制,但是显然,我们有时候在依赖一个项目的时候,我们可能只需要其中的某几个类,而不需要整个项目的所有类。这就是模块化出现的背景。

至此,Java9及之后的版本中,类的访问权限可以分为:

  • 仅本包可见(default但不导出)
  • 公开但仅本模块可见(public但不导出)
  • 完全公开(public并且导出)

但是糟糕的这个特性有传染性,需要上下游一起联动修改才能产生比较好的产出,对于既有的 JDK-8 遗老来说,这可能是一个非常难以推动的新特性。

JShell

就像Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码, 就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文 件、声明类、提供测试方法方可实现。

Interface Private Method

1
2
3
4
5
6
7
8
9
10
11
public interface Foo {

static void buzz() {
System.out.print("Hello");
staticBaz();
}

private static void staticBaz() {
System.out.println(" static world!");
}
}

Interface 在某种程度上已经完全可以取代 Class 了。

全新的 HttpClient

1
2
3
4
5
6
7
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.GET()
.build();

HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandler.asString());

JDK 10 Features

更多特性查阅 Java 10 新特性概览

Var 关键字

1
2
3
4
5
Map<Integer, String> map = new HashMap<>();

// 现在可以改成

var idToNameMap = new HashMap<Integer, String>();

容器环境感知

1
2
3
4
5
6
7
8

## 支持容器化场景
-XX:-UseContainerSupport

## 根据内场比例分配
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

JDK 11 Features

更多特性查阅 Java 11 新特性概览

新一代的垃圾回收器ZGC

ZGC, A Scalable Low-Latency Garbage Collector(Experimental) ZGC, 这应该是JDK11最为瞩目的特性, 没有之一。但是后面带了Experimental, 说明这还不建议用到生产环境。

ZGC是一个并发,基于region, 压缩型的垃圾收集器,只有 root 扫描阶段会STW(stop the world), 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。

优势

  • GC暂停时间不会超过10ms
  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
  • 和G1相比, 应用吞吐能力不会下降超过15%
  • 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
  • 初始只支持64位系统

JDK 12 Features

更多特性查阅 Java 11 新特性概览

Switch Expressions 增强的 Switch [实验性]

1
2
3
4
5
6
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}

Pattern Matching for instanceof [实验性]

1
2
3
4
5
Object obj = "Hello World!";
if (obj instanceof String) {
String s = (String) obj;
int length = s.length();
}

现在可以写成

1
2
3
if (obj instanceof String s) {
int length = s.length();
}

JDK 13 Features

多行文本 [实验性]

1
2
3
4
5
6
7
String json = """
{
"name":"mkyong",
"age":38
}
""";

Switch 支持 yield [实验性]

Switch 表达式中就多了一个关键字用于跳出 Switch 块的关键字 yield,主要用于返回一个值yield和 return 的区别在于:return 会直接跳出当前循环或者方法,而 yield 只会跳出当前 Switch 块,同时在使用 yield 时,需要有 default 条件

1
2
3
4
5
6
7
private static String descLanguage(String name) {
return switch (name) {
case "Java": yield "object-oriented, platform independent and secured";
case "Ruby": yield "a programmer's best friend";
default: yield name +" is a good language";
};
}

JDK 14 Features

record class [实验性]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 这个类具有两个特征
* 1. 所有成员属性都是final
* 2. 全部方法由构造方法,和两个成员属性访问器组成(共三个)
* 那么这种类就很适合使用record来声明
*/
final class Rectangle implements Shape {
final double length;
final double width;

public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}

double length() { return length; }
double width() { return width; }
}
/**
* 1. 使用record声明的类会自动拥有上面类中的三个方法
* 2. 在这基础上还附赠了equals(),hashCode()方法以及toString()方法
* 3. toString方法中包括所有成员属性的字符串表示形式及其名称
*/
record Rectangle(float length, float width) { }

JDK 15 Features

Sealed Classes

1
2
3
4
5
6
// 抽象类 Person 只允许 Employee 和 Manager 继承。
public abstract sealed class Person
permits Employee, Manager {

//...
}

其他

Nashorn JavaScript 引擎彻底移除:Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性。在 Java 11 中就已经被弃用,到了 Java 15 就彻底被删除了。

JDK 16 Features

没什么对开发者有太多关注的东西

JDK 17 Features

将 JEP 409:Sealed Classes(密封类)->(转正)

JDK 18 Features

没什么对开发者有太多关注的东西

JDK 19 Features

FFM API 外部函数和内存 API(预览)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. 在C库路径上查找外部函数
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixSort = linker.downcallHandle(
stdlib.lookup("radixsort"), ...);
// 2. 分配堆上内存以存储四个字符串
String[] javaStrings = { "mouse", "cat", "dog", "car" };
// 3. 分配堆外内存以存储四个指针
SegmentAllocator allocator = implicitAllocator();
MemorySegment offHeap = allocator.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
// 4. 将字符串从堆上复制到堆外
for (int i = 0; i < javaStrings.length; i++) {
// 在堆外分配一个字符串,然后存储指向它的指针
MemorySegment cString = allocator.allocateUtf8String(javaStrings[i]);
offHeap.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 5. 通过调用外部函数对堆外数据进行排序
radixSort.invoke(offHeap, javaStrings.length, MemoryAddress.NULL, '\0');
// 6. 将(重新排序的)字符串从堆外复制到堆上
for (int i = 0; i < javaStrings.length; i++) {
MemoryAddress cStringPtr = offHeap.getAtIndex(ValueLayout.ADDRESS, i);
javaStrings[i] = cStringPtr.getUtf8String(0);
}
assert Arrays.equals(javaStrings, new String[] {"car", "cat", "dog", "mouse"}); // true

virtualThread 虚拟线程(预览)

1
2
3
4
5
6
7
8
//输出线程ID 包括虚拟线程和系统线程 Thread.getId() 从jdk19废弃
Runnable runnable = () -> System.out.println(Thread.currentThread().threadId());
//创建虚拟线程
Thread thread = Thread.ofVirtual().name("testVT").unstarted(runnable);
testVT.start();
//创建虚平台线程
Thread testPT = Thread.ofPlatform().name("testPT").unstarted(runnable);
testPT.start();

JDK 20 Features

预览了一些特性

JDK 21 Features

字符串模板(预览)

1
2
3
4
5
6
7
8
9
10
11
//concatenation
message = "Greetings " + name + "!";

//String.format()
message = String.format("Greetings %s!", name); //concatenation

//MessageFormat
message = new MessageFormat("Greetings {0}!").format(name);

//StringBuilder
message = new StringBuilder().append("Greetings ").append(name).append("!").toString();

虚拟线程

虚拟线程是一项重量级的更新,一定一定要重视!虚拟线程在 Java 19 中进行了第一次预览,由JEP 425open in new window提出。JDK 20 中是第二次预览。最终,虚拟线程在 JDK21 顺利转正。

JDK 22 Features

JEP 454: 外部函数与内存 API

这一功能之前已经在 JDK 19、JDK 20 和 JDK 22 中预览,现如今在 JDK 22 中已经最终确定。它允许 Java 程序与 Java 运行时之外的代码和数据互操作。通过有效调用外部函数(即 JVM 之外的代码)和安全访问外来内存(即 JVM 不管理的内存),API 使 Java 程序能够调用本地库和处理本地数据,而不会出现 JNI 的脆性和危险。

这项功能带来一些好处:

生产力提升——用简洁、可读性强的纯 Java API 取代本地方法和 Java 本地接口 (JNI) 的脆弱机制。
性能——提供对外部函数和内存的访问,其开销可与 JNI 和 sun.misc.Unsafe 相媲美,甚至更好。
广泛的平台支持——在 JVM 运行的每个平台上都能发现和调用本地库。
统一性——提供在多种内存(如本地内存、持久内存和托管堆内存)中对结构化和非结构化数据(大小不限)进行操作的方法。
健全性——保证即使在多个线程之间分配和删除内存时,也不会出现 use-after-free 错误。
完整性——允许程序对本地代码和数据执行不安全的操作,但默认情况下会向用户发出警告。