JDK19新特性使用详解
前提
JDK19
于2022-09-20
发布GA
版本,本文将会详细介绍JDK19
新特性的使用。
新特性列表
新特性列表如下:
JPE-405
:Record
模式(预览功能)JPE-422
:JDK
移植到Linux/RISC-V
JPE-424
:外部函数和内存API
(预览功能)JPE-425
:虚拟线程,也就是协程(预览功能)JPE-426
:向量API
(第四次孵化)JPE-427
:switch
匹配模式(第三次预览)JPE-428
:结构化并发(孵化功能)
新特性使用详解
下面就每个新特性介绍其使用方式。
Record模式
使用Record
模式增强Java
编程语言以解构Record
值。可以嵌套Record
模式和Type
模式,以实现强大的、声明性的和可组合的数据导航和处理形式。这个描述看起来有点抽象,下面举几个JEP-405
的例子结合文字理解一下。以JDK16
扩展的instanceof
关键字下使用Type
模式来看:
// JDK16以前
private static void oldInstanceOf(Object x) {
if (x instanceof String) {
String s = (String) x;
System.out.println(s);
}
}
// JDK16或之后启用instanceof下的Type模式
private static void newInstanceOfTypePattern(Object x) {
if (x instanceof String s) {
System.out.println(s);
}
}
Type
模式在JDK17
和JDK18
扩展到switch
预览功能中,应用于其case
标签:
// DEMO-1
private static void switchTypePattern(String s) {
switch (s) {
case null -> System.out.println("NULL");
case "Foo", "Bar" -> System.out.println("Foo or Bar");
default -> System.out.println("Default");
}
}
// DEMO-2
interface Shape{}
class Rectangle implements Shape{}
class Triangle implements Shape{
public int calculateArea(){
return 200;
}
}
private static void switchTypePatternForShape(Shape shape) {
switch (shape) {
case null:
break;
case Rectangle r:
System.out.printf("Rectangle[%s]\n", r);
break;
case Triangle t:
if (t.calculateArea() > 100) {
System.out.printf("Large triangle[%s]\n", t);
}
default:
System.out.println("Default shape");
}
}
// DEMO-3 patterns in labels
private static void switchTypeForLabels(Object x) {
String formatted = switch (x) {
case Integer i -> String.format("int => %d", i);
case Long l -> String.format("long => %d", l);
case Double d -> String.format("double => %f", d);
case String s -> String.format("string => %s", s);
default -> x.toString();
};
}
本次的Record
模式预览功能就是基于record
关键字实现上面的Type
类型或者switch
模式。例如:
// DEMO-1
record Point(int x,int y){}
private static void printSum(Object o){
if (o instanceof Point(int x,int y)){
System.out.println(x + y);
}
}
record
类中如果存在泛型参数可以进行类型转换和推导,例如:
// DEMO-2
record Holder<T>(T target){}
// 擦除后
private void convert(Holder<Object> holder){
if (Objects.nonNull(holder) && holder instanceof Holder<Object>(String target)) {
System.out.printf("string => %s\n", target);
}
}
// 非擦除
private <T> void convert(Holder<T> holder){
if (Objects.nonNull(holder) && holder instanceof Holder<T>(String target)) {
System.out.printf("string => %s\n", target);
}
}
然后看record
和switch
结合使用:
// DEMO-3
sealed interface I permits C, D {}
final class C implements I {}
final class D implements I {}
Second<I,I> second;
private void recordSwitch() {
second = new Second<>(new D(), new C());
// second = new Second<>(new C(), new D());
switch (second) {
case Second<I, I>(C c,D d) -> System.out.printf("c => %s,d => %s", c, d);
case Second<I, I>(D d,C c) -> System.out.printf("d => %s,c => %s", d, c);
default -> System.out.println("default");
}
}
这种模式比较复杂,因为涉及到record
类、switch
模式、泛型参数并且参数类型是接口,case
子句处理的时候必须覆盖该泛型参数接口的所有子类型
不得不说,JDK引入的语法糖越来越复杂,功能看起来是强大的,但是编码的可读性在未适应期有所下降
Linux/RISC-V移植
通过Linux/RISC-V
移植,Java
将获得对硬件指令集的支持,该指令集已被广泛的语言工具链支持。RISC-V
是一种包含矢量指令的通用64
位 ISA
,目前该端口支持以下的HotSpot VM
选项:
- 模板解释器
- 客户端
JIT
编译器 - 服务端
JIT
编译器 - 包括
ZGC
和Shenandoah
在内的主流垃圾收集器
该移植基本已经完成,JEP
的重点是将该端口集成到JDK
的主仓库中。
外部函数和内存API
外部函数和内存API
的主要功能是引入一组API
,Java
程序可以通过该组API
与Java
运行时之外的代码和数据进行交互。有以下目标:
- 易用性:通过卓越的纯
Java
开发模型代替JNI
- 高性能:提供能与当前
JNI
或者Unsafe
相当甚至更优的性能 - 通用性:提供支持不同种类的外部内存(如本地内存、持久化内存和托管堆内存)的
API
,并随着时间推移支持其他操作系统甚至其他语言编写的外部函数 - 安全性:允许程序对外部内存执行不安全的操作,但默认警告用户此类操作
核心的API
和功能如下:
- 分配外部内存:
MemorySegment
、MemoryAddress
和SegmentAllocator
- 操作和访问结构化的外部内存:
MemoryLayout
和VarHandle
- 控制外部内存:
MemorySession
- 调用外部函数:
Linker
、FunctionDescriptor
和SymbolLookup
这些API
统称为FFM API
,位于java.base
模块的java.lang.foreign
包中。由于API
比较多并且不算简单,这里只举一个简单的例子:
public class AllocMemoryMain {
public static void main(String[] args) {
new AllocMemoryMain().allocMemory();
}
/**
* 分配内存
* struct Point {
* int x;
* int y;
* } pts[10];
*/
public void allocMemory() {
Random random = new Random();
// 分配本地内存
MemorySegment segment = MemorySegment.allocateNative(2 * 4 * 10, MemorySession.openImplicit());
// 创建顺序内存布局
SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")));
// 对内存设置值
VarHandle xHandle = ptsLayout.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("x"));
VarHandle yHandle = ptsLayout.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("y"));
for (int i = 0; i < ptsLayout.elementCount(); i++) {
int x = i * random.nextInt(100);
int y = i * random.nextInt(100);
xHandle.set(segment,/* index */ (long) i,/* value to write */x); // x
yHandle.set(segment,/* index */ (long) i,/* value to write */ y); // y
System.out.printf("index => %d, x = %d, y = %d\n", i, x, y);
}
// 获取内存值
int xValue = (int) xHandle.get(segment, 5);
System.out.println("Point[5].x = " + xValue);
int yValue = (int) yHandle.get(segment, 6);
System.out.println("Point[6].y = " + yValue);
}
}
// 某次执行输出结果
index => 0, x = 0, y = 0
index => 1, x = 79, y = 16
index => 2, x = 164, y = 134
index => 3, x = 150, y = 60
index => 4, x = 152, y = 232
index => 5, x = 495, y = 240
index => 6, x = 54, y = 162
index => 7, x = 406, y = 644
index => 8, x = 464, y = 144
index => 9, x = 153, y = 342
Point[5].x = 495
Point[6].y = 162
FFM API
是一组极度强大的API
,有了它可以灵活地安全地使用外部内存和外部(跨语言)函数。
虚拟线程
虚拟线程,就是轻量级线程,也就是俗称的协程,虚拟线程的资源分配和调度由VM
实现,与平台线程(platform thread
)有很大的不同。从目前的源代码来看,虚拟线程的状态管理、任务提交、休眠和唤醒等也是完全由VM
实现。可以通过下面的方式创建虚拟线程:
// 方式一:直接启动虚拟线程,因为默认参数原因这样启动的虚拟线程名称为空字符串
Thread.startVirtualThread(() -> {
Thread thread = Thread.currentThread();
System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
});
// 方式二:Builder模式构建
Thread vt = Thread.ofVirtual().allowSetThreadLocals(false)
.name("VirtualWorker-", 0)
.inheritInheritableThreadLocals(false)
.unstarted(() -> {
Thread thread = Thread.currentThread();
System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
});
vt.start();
// 方式三:Factory模式构建
ThreadFactory factory = Thread.ofVirtual().allowSetThreadLocals(false)
.name("VirtualFactoryWorker-", 0)
.inheritInheritableThreadLocals(false)
.factory();
Thread virtualWorker = factory.newThread(() -> {
Thread thread = Thread.currentThread();
System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
});
virtualWorker.start();
// 可以构建"虚拟线程池"
ExecutorService executorService = Executors.newThreadPerTaskExecutor(factory);
由于虚拟线程的功能还处于预览阶段,创建协程的时候无法自定义执行器(准确来说是运载线程),目前所有虚拟线程都是交由一个内置的全局ForkJoinPool
实例执行,实现方式上和JDK8
中新增的并行流比较接近。另外,目前来看虚拟线程和原来的JUC
类库是亲和的,可以把虚拟线程替换原来JUC
类库中的Thread
实例来尝试使用(在生产应用建议等该功能正式发布)
向量API
向量API
目前是第四次孵化,功能是表达向量计算,在运行时编译为CPU
架构上的最佳向量指令,从而实现优于等效标量计算的性能。目前相关API
都在jdk.incubator.vector
包下,使用的例子如下:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
private static void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) {
var m = SPECIES.indexInRange(i, a.length);
var va = FloatVector.fromArray(SPECIES, a, i, m);
var vb = FloatVector.fromArray(SPECIES, b, i, m);
var vc = va.mul(va).add(vb.mul(vb)).neg();
vc.intoArray(c, i, m);
}
}
public static void main(String[] args) {
float[] a = new float[]{1.0f, 3.0f, 2.0f};
float[] b = {1.0f, -1.0f, 5.0f};
float[] c = {1.0f, 6.0f, 1.0f};
vectorComputation(a, b, c);
System.out.println(Arrays.toString(c));
}
Vector
有很多特化子类,可以通过不同的VectorSpecies
进行定义。
switch匹配模式
switch
匹配模式第三次预览,主要是对匹配模式进行了扩展。主要有几点改进:
- 增强类型校验,
case
子句支持多种类型
record Point(int i, int j) {}
enum Color { RED, GREEN, BLUE; }
private void multiTypeCase(Object o) {
switch (o) {
case null -> System.out.println("null");
case String s -> System.out.println("String");
case Color c -> System.out.println("Color: " + c.toString());
case Point p -> System.out.println("Record class: " + p.toString());
case int[] ia -> System.out.println("Array of ints of length" + ia.length);
default -> System.out.println("Something else");
}
}
- 增强表达式和语句的表现力和适用性,可以实现
selector
模式
private int selector(Object o) {
return switch (o) {
case String s -> s.length();
case Integer i -> i;
default -> 0;
};
}
- 扩展模式变量声明范围
private void switchScope(Object o) {
switch (o) {
case Character c
when c.charValue() == 7:
System.out.println("Seven!");
break;
default:
break;
}
}
- 优化
null
处理
private void switchNull(Object o) {
switch (o) {
case null -> System.out.println("null!");
case String s -> System.out.println("String");
default -> System.out.println("Something else");
}
}
结构化并发
结构化并发功能在孵化阶段,该功能旨在通过结构化并发库来简化多线程编程。结构化并发提供的特性将在不同线程中运行的多个任务视为一个工作单元,以简化错误处理和取消,提高了可靠性和可观测性。
record User(String name, Long id){}
record Order(String orderNo, Long id){}
record Response(User user, Order order){}
private User findUser(){
throw new UnsupportedOperationException("findUser");
}
private Order fetchOrder(){
throw new UnsupportedOperationException("fetchOrder");
}
private Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<User> user = scope.fork(() -> findUser());
Future<Order> order = scope.fork(() -> fetchOrder());
scope.join(); // Join both forks
scope.throwIfFailed(); // ... and propagate errors
// Here, both forks have succeeded, so compose their results
return new Response(user.resultNow(), order.resultNow());
}
}
参考资料
JDK 19
:https://openjdk.org/projects/jdk/19
,文中直接应用部分文档描述的翻译
(本文完,c-2-d e-a-20220923)
JDK19新特性使用详解的更多相关文章
- HTML5新特性及详解
什么是HTML5:HTML5 是下一代的HTML,将成为 HTML.XHTML 以及 HTML DOM 的新标准. 为 HTML5 建立的一些规则: 新特性应该基于 HTML.CSS.DOM 以及 J ...
- iOS7新特性-NSURLSession详解
前言:本文由DevDiv版主@jas 原创翻译,转载请注明出处!原文:http://www.shinobicontrols.com/b ... day-1-nsurlsession/ 大家都知道,过去 ...
- Php5.5新特性 Generators详解
在PHP5.5.0版本中,新增了生成器(Generators)特性,用于简化实现迭代器接口(Iterator)创建简单的迭代器的复杂性. 通过生成器,我们可以轻松的使用foreach迭代一系列的数据, ...
- Java8新特性--lamada详解
最近玩了一下这个,感觉挺有趣的,语法使用起来很简洁,让代码看起来挺清爽易读的. 看了一下源码,发现挺充分的利用了jak1.5的特性(注解.泛型). 但是,具体的实现流程还是有点不通透,先Mark,等用 ...
- Servlet3.0新特性使用详解
可插拔的Web框架 几乎所有基于Java的web框架都建立在servlet之上.现今大多数web框架要么通过servlet.要么通过Web.xml插入.利用标注(Annotation)来定义servl ...
- Java8新特性: CompletableFuture详解
CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调.流式处理.多个Future组合处理的能力,使Java在处理多任务的 ...
- ios新特征 ARC详解
IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报 目录(?)[+] 关闭工程的ARC(Automatic Reference Co ...
- ios中键值编码kvc和键值监听kvo的特性及详解
总结: kvc键值编码 1.就是在oc中可以对属性进行动态读写(以往都是自己赋值属性) 2. 如果方法属性的关键字和需要数据中的关键字相同的话 ...
- [转]Oracle 11g R2 RAC高可用连接特性 – SCAN详解
原文地址:http://czmmiao.iteye.com/blog/2124373 昨天帮朋友解决11g RAC SCAN问题,当时为这朋友简单解答了一些SCAN特性相关的问题,但我知道这仅仅是 ...
随机推荐
- Windows 下如何调试 PowerShell
背景 最近在用 PowerShell 的时候,发现一些地方特别有意思.于是就萌生了看源代码的想法,单看肯定不过瘾,调试起来才有意思.于是就有了这个,记录下. 调试 PowerShell 主要分为两种方 ...
- Java学习_常见异常
JAVA常见异常 Java.io.NullPointerException null 空的,不存在的 NullPointer 空指针 空指针异常,该异常出现在我们操作某个对象的属性或方法时,如果该对象 ...
- filebeat + logstash 日志采集链路配置
1. 概述 一个完整的采集链路的流程如下: 所以要进行采集链路的部署需要以下几个步聚: nginx的配置 filebeat部署 logstash部署 kafka部署 kudu部署 下面将详细说明各个部 ...
- P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具
[HAOI2008]移动玩具 题目描述 在一个 4 × 4 4\times4 4×4 的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方 ...
- Apache DolphinScheduler新一代分布式工作流任务调度平台实战-上
概述 定义 dolphinscheduler 官网地址 https://dolphinscheduler.apache.org/ dolphinscheduler GitHub地址 https://g ...
- 小白之Python基础(二)
一.字符串 1.字符串编码发展: 1)ASCII码: 一个字节去表示 (8个比特(bit)作为一个字节(byte),因此,一个字节能表示的最大的整数就是255(二进制11111111 = 十进制255 ...
- 在Linux下源码编译安装GreatSQL/MySQL
欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 本 ...
- 论语音社交视频直播平台与 Apache DolphinScheduler 的适配度有多高
在 Apache DolphinScheduler& Apache ShenYu(Incubating) Meetup 上,YY 直播 软件工程师 袁丙泽 为我们分享了<YY直播基于Ap ...
- 完成 DolphinScheduler 新手任务赢好礼活动 | 倒计时3 天
想轻松参与 DolphinScheduler 项目贡献吗? 想获得 500 元京东购物卡吗? 参与活动,有机会得更多活动奖励! 活动截止至6月30日 了解更多详情: 在你参与 DolphinSched ...
- DolphinScheduler 新晋 Committer 成员啦
Apache DolphinScheduler在2021年的3月18号正式成为 Apache 顶级项目后,就又迎来了好消息,经过Apache DolphinScheduler PMC们的推荐和投票, ...