Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

Java 14 新特性

jdk 14 主要的新特性参考:
openjdk JDK 14

baeldung New Features in Java 14

JDK 14 的新特性

Java 14 于2020年3月17日发布,包含新特性:

早期版本实验特性

Switch Expressions (JEP 361)

switch 表达式终于成为正式特性了,从JDK12引入,JDK13迭代,终于成为了标准的特性了。
这意味着switch 表达式可以用在生产环境了,而不是仅仅在预览模式下由开发来试试。

还是上次那个例子,判断是否是节假日,之前的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
boolean isTodayHoliday;
switch (day) {
case "MONDAY":
case "TUESDAY":
case "WEDNESDAY":
case "THURSDAY":
case "FRIDAY":
isTodayHoliday = false;
break;
case "SATURDAY":
case "SUNDAY":
isTodayHoliday = true;
break;
default:
throw new IllegalArgumentException("What's a " + day);
}

如今可以使用switch 表达式,更简介明了(注意直接赋值)

1
2
3
4
5
boolean isTodayHoliday = switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> false;
case "SATURDAY", "SUNDAY" -> true;
default -> throw new IllegalArgumentException("What's a " + day);
};

掌握吧,已经可以直接用了。

Text Blocks (JEP 368)

好消息是第二个版本了,坏消息是依然是预览特性。这个版本新增了两个转意:

  • \ : 代表行还没有结束
  • \s: 代表一个空字符

例子

1
String multiline = "A quick brown fox jumps over a lazy dog; the lazy dog howls loudly.";

现在可以写成这样

1
2
3
String multiline = """
A quick brown fox jumps over a lazy dog; \
the lazy dog howls loudly.""";

只是让语句变的更可读,并没有在 dog后面加入一个新行。(这就是各类命令行常用的啊)

新的实验特性

Pattern Matching for instanceof (JEP 305)

jdk 14 中新增了 instanceof的模式匹配特性,来让程序员的人生在轻松那么一些。

ps. 我记得我在jdk12中提到过这一特性,又确认了一下,应该是当时搞错了,这个特性确认是在 jdk14中才加入的。出错的出处也找到了,是这里搞错了
另外该特性是在 JDK 16 中正式发布的

有多轻松呢?这么轻松:

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

现在我们可以这么写

1
2
3
4
if (obj instanceof String str) {
int len = str.length();
// ...
}

看一眼可以了,预览特性。

Records (JEP 359)

引入Records 可以帮助我们减少不可变数据模型中一堆无意义的重复的模板代码。我觉得 JEP 359中的Motivation and Goals写的很清楚,大意就是:

都说java太啰嗦太多规范限定,写一个数据载体模型需要写一堆无意义的 get/set/equals/hashcode/toString 等方法,虽然IDE会帮忙处理这些重复的代码,
但是无法帮助代码阅读的人在一堆代码里面提取出 这个类只是一个数据的载体 这样的结论。写一个java数据模型应该是简单的:简单写,简单读,简单的判断是否有错误。

例子,定一个 User 模型,包含 id 和 password 属性。

1
public record User(int id, String password) { }

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private User user = new User(0, "UserOne");

@Test
public void givenRecord_whenObjInitialized_thenValuesCanBeFetchedWithGetters() {
assertEquals(0, user1.id());
assertEquals("UserOne", user1.password());
}

@Test
public void whenRecord_thenEqualsImplemented() {
User user2 = user1;
assertTrue(user1, user2);
}

@Test
public void whenRecord_thenToStringImplemented() {
assertTrue(user1.toString().contains("UserOne"));
}

可以看见对 id/password 的获取不是用 getXxx了,而是直接用数据名同名的方法名。没有set因为是不可变数据类型。

record 还可以覆盖改造方法、创建静态类、定义自己的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public record User(int id, String password) {
//再定义一个构造方法
public User(int id) {
this(id, null);
}

//额外定义的方法
public String sayHello() {
return "hello";
}

//静态方法
public static String IdAddPassword(User user) {
return user.id + user.password;
}
}

总结下,感觉就是lombok@Data,加了数据不可变的限制。

新的正式特性

Helpful NullPointerExceptions (JEP 358)

以前NullPointerException的异常栈并没有包含多少信息来告诉我们到底哪里NPE了,只告诉了我们异常的类以及对应的代码行。
现在Java 14增加了直接指出哪里NPE的能力,比如:

1
2
int[] arr = null;
arr[0] = 1;

如果在以前

1
2
Exception in thread "main" java.lang.NullPointerException
at com.baeldung.MyClass.main(MyClass.java:27)

而现在的报错是这样子的

1
java.lang.NullPointerException: Cannot store to int array because "a" is null

我们可以准确的知道哪一个字段导致了NPE。
我们在用刚才那个Record试一下:

1
2
3
4
5
6
@Test
public void testNPE(){
User user = new User(1);
System.out.println(user.password().toUpperCase());
}

输出

1
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because the return value of "com.forg.java14.pojo.User.password()" is null

很明确的就指出了哪个字段是null,舒服~

孵化中的特性

孵化中的特性不同于预览特性,都单独放在了独立模块的jdk.incubator包下面

Foreign Memory Access API (JEP 370)

Java需要访问堆内存之外的内存空间是,通常有两种方法:

  • java.nio.ByteBuffer
  • sun.misc.Unsafe

java.nio.ByteBuffer允许使用allocateDirect()方法在堆内存之外分配内存空间。但是 ByteBuffer 大小不能超过2G,释放内存需要依赖垃圾回收器。
sun.misc.Unsafe 的使用是不安全的,可能会造成JMV崩溃,另外Unsafe 并不推荐使用。

JEP 370 引入了一套高效的API来访问外部内存地址,目前还是在孵化阶段,相关API在jdk.incubator.foreign模块的jdk.incubator.foreign包中。
该API包含三个核心接口: MemorySegmentMemoryAddressMemoryLayout

MemorySegment接口表示一个连续的内存区域,它有空间上和时间上的约束:空间上的约束指的是不能访问所分配内存空间之外的地址;时间上的约束指的是当MemorySegment被关闭之后,不能继续对它进行操作。关闭一个MemorySegment会释放内存。
MemoryAddress接口描述在MemorySegment中的相对位置。通常是用法是通过MemorySegment.baseAddress()方法得到起始地址,再使用offset(long l)方法移到到新的地址。
MemoryLayout接口描述MemorySegment中的内存布局。布局的最小单元有两种:表示单个值的ValueLayout,顺序布局SequenceLayout。

Packaging Tool (JEP 343)

通常java交付只需要给到对应的jar文件,然后在自己的JVM中运行,但是大部分人希望通过双击安装来在自己的平台上,就比如 windows和 macOS 。
JEP 343 就是这个目的,开发者可以使用 jlink 来把最小化依赖的JDK模块压缩打包,然后创建一个轻量化的镜像,比如windows系统的exe,macOS的dmg 。

JVM/HotSpot 特性

ZGC on Windows (JEP 365) and macOS (JEP 364) – Experimental

早在java 11引入的实验性的ZGC一直都只支持 Linux/x64 平台。
在java 14中已经支持了Windows与macOS,虽然只是个实验性的特性,以后终将会在正式版本中发布的。

NUMA-Aware Memory Allocation for G1 (JEP 345)

不像并行回收器,G1垃圾回收器至今没有实现 Non-uniform memory access (NUMA) ,在这次JEP中增加G1的NUMA支持。

JFR Event Streaming (JEP 349)

通过这一增强,JDK的飞行记录器数据现在被公开,以便可以持续监控。这涉及到对包jdk.jfr.cosumer的修改,以便用户现在可以直接读取或流式传输记录数据。

弃用或者删除的特性

弃用的

  • Solaris and SPARC Ports (JEP 362) – because this Unix operating system and RISC processor are not in active development since the past few years
  • ParallelScavenge + SerialOld GC Combination (JEP 366) – since this is a rarely used combination of GC algorithms, and requires significant maintenance effort
    删除的
  • Concurrent Mark Sweep (CMS) Garbage Collector 在java 9 的时候已经被标记弃用,其代替者G1已经是默认的垃圾回收器了,而且还有其他很多可选的 ZGC,Shenandoah 。所以直接删除了。
  • Pack200 Tools and API (JEP 367) – these were deprecated for removal in Java 11, and now removed

总结一下

正式特性有

  • Switch Expressions
  • Helpful NullPointerExceptions

参考

JDK 14

New Features in Java 14

一文看尽JDK 14全部新特性