一帆磨砺

生活所迫,一叶孤舟

0%

Java版本特性-JDK14

引用参考文档链接

  1. jdk14特性列表

写在开头

  1. switch已经可以正式使用,用起来很爽
  2. 官方开放了API可以访问堆外的外部内存,这个特性看起来在大数据领域可以发挥很大的作用
  3. 空指针异常信息的细节增强非常方便开发人员的本地开发过程,大大的简化了本地调试的工作

instanceof增强(预调整)

目前instanceof大多数使用方式如下所示:

1
2
3
4
if (obj instanceof String) {
String s = (String) obj;
// use s
}
  1. 先判断是否是String类型
  2. 类型强转并赋值新变量
  3. 变量使用
    增强后,将1-2步骤合并,如下所示:
    1
    2
    3
    4
    5
    if (obj instanceof String s) {
    // can use s here
    } else {
    // can't use s here
    }
    该特性属于代码简化,增加代码可读性。
    目前JDK14未开放使用。

新增一个打包工具

基于JavaFX的打包工具,能够将Java应用程序打包成各系统对应的应用类型,如exe、dmg、dep等。应该是针对客户端软件的更新。

G1增强:NUMA-Aware 内存分配

随着使用非统一内存访问架构(NUMA)的机器越来越多,G1将针对NUMA场景处理增强

  1. 年轻代将保证在同一NUMA节点分配内存,如果内存不够将触发垃圾回收
  2. 老年代不需要保证在同一NUMA节点分配内存
  3. Humongous区域不适用该策略
    通过启动参数+XX:+UseNUMA开启

JFR增强:事件流

在JDK14之前,JFR都是通过命令手动开启录制,转储文件,再进行分析,现在开放了API,可以通过Java代码进行持续的监控,接下来举例两种使用方式(还有其他使用方式)。agent领域的开发同学狂喜。

RecordingStream

1
2
3
4
5
6
7
8
9
10
11
try (var rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
rs.onEvent("jdk.CPULoad", event -> {
System.out.println("func1:jdk.CPULoad:" + event.getFloat("machineTotal"));
});
rs.onEvent("jdk.JavaMonitorEnter", event -> {
System.out.println("func1:jdk.JavaMonitorEnter:" + event.getClass("monitorClass"));
});
rs.start();
}

该方法可以直接进行指定的参数录制,并在对应的事件中触发,可由自定义代码收集

EventStream::openRepository()

1
2
3
4
5
6
7
8
9
10
11
try {
EventStream eventStream = EventStream.openRepository();
try (eventStream) {
eventStream.onEvent(recordedEvent -> {
System.out.println("func2:getEventType:" + recordedEvent.getEventType().getName());
});
eventStream.start();
}
} catch (IOException e) {
throw new RuntimeException(e);
}

该方式需要在进程外通过命令行或其他方式开启JFR才能够开始录制。

硬件适配增强:Non-Volatile Mapped Byte Buffers

针对 Linux/x64 和 Linux/AArch64 的非易失性存储器 (NVM)场景下,应用程序可以有效且连贯的访问和更新NVM。
官方文档说

NVM offers the opportunity for application programmers to create and update program state across program runs without incurring the significant copying and/or translation costs that output to and input from a persistent medium normally implies. This is particularly significant for transactional programs, where regular persistence of in-doubt state is required to enable crash recovery.

跨程序的数据交互场景,笔者想不到能够容易理解的业务场景。这中特定的硬件组合,和JDK10提出的Heap Allocation on Alternative Memory Devices很类似,应该在大数据集群处理的业务场景可能会有用处?

空指针异常信息增强

以前的空指针异常信息只显示到某一行,如果某一行的变量比较多,是没办法直接定位哪个变量为null的,这时候基本上都需要本地再运行debug才行,而JDK14中,通过启动参数-XX:+ShowCodeDetailsInExceptionMessages可以开启更详细的空指针异常信息.
需要注意的是,官方文档提出这种详细信息是经过了额外的计算的,因此性能方面需要考虑自己的应用空指针异常是否频繁,避免带来额外的性能开销。

异常示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class NPE {
public static void func() {
C c = new C();
c.b = new B();
try {
c.b.a.name = "null";
} catch (Exception e) {
e.printStackTrace();
}
try {
B.test().arr[1][2] = 3;
} catch (Exception e) {
e.printStackTrace();
}
}

public static class A {
public String name;

public int[][] arr;
}

public static class B {
public A a;

public static A test() {
return new A();
}
}

public static class C {
public B b;
}
}

异常信息

开启前

1
2
3
4
5
6
7
java.lang.NullPointerException
at com.janwarlen.feature.NPE.func(NPE.java:9)
at com.janwarlen.Demo.main(Demo.java:15)
java.lang.NullPointerException
at com.janwarlen.feature.NPE.func(NPE.java:14)
at com.janwarlen.Demo.main(Demo.java:15)

开启后

1
2
3
4
5
6
java.lang.NullPointerException: Cannot assign field "name" because "c.b.a" is null
at com.janwarlen.feature.NPE.func(NPE.java:9)
at com.janwarlen.Demo.main(Demo.java:15)
java.lang.NullPointerException: Cannot load from object array because "com.janwarlen.feature.NPE$B.test().arr" is null
at com.janwarlen.feature.NPE.func(NPE.java:14)
at com.janwarlen.Demo.main(Demo.java:15)

新的数据建模方式(预调整)

目前想要封装一个数据模型类,有很多方法需要重复的编码,如equalshashcodetoString等。因此JDK14中预添加了该技术特性(添加但未开放使用),以便开发人员更简单的创建数据模型类。因未开放使用,因此只能从官方文档上简单的阅读,从表面上看是简化了数据模型的类的创建,但是看起来实际使用效果并不会太好,因为数据模型类的创建,那些重复编码的函数并不是非常重要的部分,继承多态、工程结构才是。使用该特性是有额外的限制的,比如:不能继承于其他的类,不能为抽象类,并且是隐式的final等等。综合对比,似乎该特性即便开放使用,也只能在有限的场景里发挥作用。

Switch增强(正式开发使用)

前两次JDK的预调整终于开放使用,放Demo代码

增强前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void func1(String arg) {
switch (arg) {
case "1":
case "2":
case "3":
System.out.println("small");
break;
case "4":
case "5":
case "6":
System.out.println("big");
break;
default:
System.out.println("illegal");
break;
}
}
public void func2(int e) {
int x;
switch (e) {
case 1:
x = 111;
break;
case 2:
x = 222;
break;
case 3:
x = 233;
break;
default:
x = 666;
break;
}
System.out.println(x);
}

增强后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void func1Enhanced(String arg) {
switch (arg) {
case "1", "2", "3" -> System.out.println("small");
case "4", "5", "6" -> System.out.println("big");
default -> System.out.println("illegal");
}
}
public void func2Enhanced(int e) {
int x = switch (e) {
case 1 -> 111;
case 2 -> 222;
case 3 -> 233;
default -> 666;
};
System.out.println(x);
}

yield

官方文档没有明确说明yield的作用,从示例代码来看(和之前JDK版本的说明),该关键字使用场景为case中执行语句有多条,但是又必须返回值的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void func3(String yield) {
int d = switch (yield) {
case "MONDAY" -> {
System.out.println("monday");
yield 1;
}
case "TUESDAY" -> {
System.out.println("TUESDAY");
yield 2;
}
default -> {
System.out.println("not monday");
yield 0;
}
};
System.out.println("func3:" + d);
}

弃用 Solaris and SPARC Ports

不太明白这俩是干嘛的,也没有搜到有博主分享关于这两个的文档,根据官方文档的这段话

Dropping support for these ports will enable contributors in the OpenJDK Community to accelerate the development of new features that will move the platform forward.

看着像是针对特殊的系统平台类的兼容,该弃用应该也是为了更方便的迭代和扩展。

删除垃圾回收器: CMS(Concurrent Mark Sweep)

G1已经可以完全替代CMS,从性能和效率各个方面,JDK9标注废弃,等到这个版本才正式删除。

ZGC移植:macOS、Windows(实验性)

方便开发人员在本地调试,也针对开发桌面客户端软件的用户能够使用ZGC。

垃圾回收算法弃用:ParallelScavenge + SerialOld

官方假设使用这种算法组合的人较少才进行弃用,如果后续反馈的人数较多,可能会重新启用。
官方认为这种组合仅适用于具有非常大的年轻一代和非常小的老一代的部署。
涉及启动参数:-XX:+UseParallelGC -XX:-UseParallelOldGC -XX:UseParallelOldGC

删除Pack200及其API

JDK11弃用的,在本此JDK迭代中彻底删除
该针对jar包的压缩方案已经不再适用于现在的各种场景。

文本块(第二次预调整)

为了更好地控制换行符和空格的处理,我们引入了两个新的转义序列。

当前行末尾添加\表示不换行

该转义仅在文本块中生效。

1
2
3
String literal = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit, sed do eiusmod tempor incididunt ut labore " +
"et dolore magna aliqua.";

等同于

1
2
3
4
5
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
""";

\s

等效于’ ‘。该转义在String中都可以使用。
应该是针对String::stripIndent()和trim()提出的,Escape sequences aren't translated until after incident space stripping,使用\s防止文本块中行末的空格被清空。

添加API以便Java可以访问堆外外部内存

  1. 避开垃圾回收器维护大量的缓存
  2. 跨进程通信
  3. 映射文件到内存
    等等场景皆可使用,不过官方仅是给出API可以访问堆外的外部内存,具体的功能实现还需要用户自己去封装。
    核心三个类MemorySegment/MemoryAddress/MemoryLayout

欢迎关注我的其它发布渠道