目录
概述
变动说明
重要变更和信息
下载地址
Java18新特性总结
1、JEP 420: Switch 的模式匹配(第二次预览)
功能进化
Switch 模式匹配
类型标签
null标签
守卫标签
2、JEP 400:默认UTF-8编码
3、JEP 408:简易Web服务器
jwebserver的参数
示例1
准备页面
启动
4、JEP 413:支持在 Java API 文档中加入代码片段
5、JEP 416:用方法句柄重新实现核心反射
6、JEP 417:向量 API(第三次孵化)
功能进化
7、JEP 418:互联网地址解析 SPI
8、JEP 419:外部函数和内存 API(第二次孵化)
功能进化
9、JEP 421:弃用 Finalization 功能
10、移除的APIs、工具、容器
概述
JDK 18 于 2022 年 3 月 22 日正式发布。
JEP(Java Enhancement Proposal)Java增强提案
CSR(Compatibility & Specification Review) 兼容性和规范审查
变动说明
官网:
Java Platform, Standard Edition Java Language Updates, Release 18
JDK 18 Release Notes, Important Changes, and Information
JDK 18
https://blogs.oracle.com/java/post/the-arrival-of-java-18
更多参考:
JDK 18 Documentation - Home 更多版本:Java Platform, Standard Edition Documentation - Releases
Java Platform, Standard Edition Oracle JDK Migration Guide, Release 18
重要变更和信息
JDK 18 包含 9 个 新特性 ,分别为:
-
JEP 400: UTF-8 by Default 默认UTF-8编码
-
JEP 408: Simple Web Server 简易Web服务器
-
JEP 413: Code Snippets in Java API Documentation 支持在 Java API 文档中加入代码片段
-
JEP 416: Reimplement Core Reflection with Method Handles 用方法句柄重新实现核心反射
-
JEP 417: Vector API (Third Incubator) 向量 API(第三次孵化)
-
JEP 418: Internet-Address Resolution SPI 互联网地址解析 SPI
-
JEP 419: Foreign Function & Memory API (Second Incubator) 外部函数和内存 API(第三次孵化)
-
JEP 420: Pattern Matching for switch (Second Preview) Switch 的模式匹配(第二次预览)
-
JEP 421: Deprecate Finalization for Removal 弃用 Finalization 功能
而其中与开发过程中直接相关的特性主要包括:JEP 420(Switch 表达式的模式匹配)等。
下载地址
你可以从这个链接下载Oracle JDK
版本,更多版本下载。
也可以从这个链接下载生产就绪的OpenJDK
版本。文件为压缩包,解压并设置环境变量就可以使用。
Java18新特性总结
1、JEP 420: Switch 的模式匹配(第二次预览)
JEP 420 (specification/language
)
这仍然是一个预览特性
功能进化
switch
表达式的功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 5 | 首次引入,仅支持整型数据类型(如 byte , short , char , 和 int )及枚举类型 | ||
Java 7 | 支持 String 类型 | ||
Java 12 | 预览特性 | JEP 325 | 支持Switch 表达式(箭头函数) |
Java 13 | 预览特性 | JEP 354 | 加入 yield 语句来替代 break 语句,用于从 switch 表达式返回值 |
Java 14 | 正式特性 | JEP 361 | 前2个版本的新特性转为正式特性 |
Java 17 | 预览特性 | JEP 406 | 引入模式匹配的Switch 表达式作为预览特性 |
Java 18 | 第二次预览 | JEP 420 | 调整优化 |
在 Java 16 中, JEP 394
将 instanceof
的模式匹配发布为正式属性。虽然可以不需要强制转换了,但是仍然需要大量的 if...else
。而 Switch 表达式虽然简化了if...else
,但是它无法像instanceof
一样不需要强制转换。为了解决这个痛点,Java 17
引入模式匹配的Switch
表达式特性 ,目前该特性为预览特性。
该特性扩展了 switch 表达式和语句,允许它们使用模式匹配,这就意味着我们可以在 switch
的 case 标签中使用模式,如类型模式,使得代码更加灵活和表达性更强。而且也无需进行显式的类型转换了。例如,可以使用 case Integer i
这样的语法来匹配并自动转换类型。
但是,不知道小伙伴们注意没有,Switch
表达式只有一种类型,比如:我有一个诗人类(Poet
),它有3个实现类:唐朝诗人(TangPoet
)、宋朝诗人(SongPoet
)、汉朝诗人(HanPoet
),我要根据诗人的类型进行不同处理 :
Poet poet = ... // 诗人switch (poet.getClass().getName()) {case "my.poet.TangPoet":TangPoet tp = (TangPoet) obj;// 处理唐朝诗人break;case "my.poet.SongPoet":SongPoet sp = (SongPoet) obj;// 处理宋朝诗人break;case "my.poet.HanPoet":HanPoet hp = (HanPoet) obj;// 处理汉朝诗人break;// 其他类型}
这个强转显然比较麻烦。所以,参考Java 17
中,参考instanceof
的模式匹配,为switch
表达式引入了模式匹配功能作为预览特性。
Switch 模式匹配
在 Java 17 中,switch
表达式允许使用模式匹配来处理对象类型,这样就可以直接在 switch
语句中检查和转换类型,而不需要额外的 if...else
结构和显式类型转换。
case
后面可以跟的标签主要有:
-
类型标签
-
null标签
-
守卫标签
-
enum或常量值
类型标签
允许在 switch
语句的 case 分支中直接匹配对象的类型。例如,case String s
允许你在该分支中直接作为字符串类型的 s
来使用,避免了显式的类型检查和强制类型转换。
举个例子:
@Testpublic void switchTest() {// 不是用switch根据类型判断Object[] objects = { "Hello", "Java", "17", 666, 0.618 };for (Object obj : objects) {if (obj instanceof Integer v) {System.out.printf("为整型 :%s %n", v);} else if (obj instanceof Float v) {System.out.printf("为Float:%s %n", v);} else if (obj instanceof Double v) {System.out.printf("为Double:%s %n", v);} else if (obj instanceof String v) {System.out.printf("为字符串:%s %n", v);} else {System.out.printf("其他类型:%s %n", obj);}}}
我们用 Switch
表达式来改造下:
@Testpublic void switchTest() {Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };for (Object obj: objects) {switch (obj) {case Integer v -> System.out.println("为整数型:" + v);case Float v -> System.out.println("为浮点型:" + v);case Double v -> System.out.println("为双精度浮点数:" + v);case String v -> System.out.println("为字符串:" + v);default -> System.out.println("其他类型:" + obj);}}}
相比上面的 if...else
简洁了很多。同时在 Java 17
之前,Switch
选择器表达式只支持特定类型,即基本整型数据类型byte
、short
、char
和int
;对应的装箱形式Byte
、Short
、Character
和Integer
;String
类;枚举类型。现在有了类型模式,Switch
表达式可以是任何类型啦。
null标签
当switch
允许任何引用类型的选择器表达式,那么我们需要留意null
的情况,在Java17
之前,向switch
语句传递一个null
值,会抛出一个NullPointerException
,现在可以通过类型模式,将 null 检查作为一个单独的case标签来处理,如下:
@Testpublic void switchTest() {Object[] objects = { "Hello", "Java", "17", 142857, 0.618 };for (Object obj: objects) {switch (obj) {case Integer v -> System.out.println("为整数型:" + v);case Float v -> System.out.println("为浮点型:" + v);case Double v -> System.out.println("为双精度浮点数:" + v);case String v -> System.out.println("为字符串:" + v);case null -> System.out.println("为空值");default -> System.out.println("其他类型:" + obj);}}}
case null
可以直接匹配值为 null
的情况。
守卫标签
与匹配常量值的case
标签不同,模式case
标签可以对应多个变量值。这通常会导致switch
规则右侧出现条件语句。
根据字符串长度判断诗句是五言绝句还是七言绝句,代码如下:
@Testpublic void switchCaseCaseTest() {String[] poems = { "千山鸟飞绝", "春城无处不飞花", "红豆生南国", "二月春风似剪刀","念奴娇" };for (String poem : poems) {switch (poem) {case null -> System.out.println("为空值");case String s -> {if (s.length() == 5)System.out.printf("五言绝句:%s%n", s);else if (s.length() == 7)System.out.printf("七言绝句:%s%n", s);elseSystem.out.printf("不知道是啥:%s%n", s);}}}}
这里的问题是,使用单一模式(即类型)来区分case
就只能判断一种情况。我们只能在模式匹配中再通过if……else
判断来区分不同的情况,来对一个模式的细化。这时,我们可以是使用switch
中的when
子句指定模式case
标签的条件,例如,case String s when if (s.length() == 5)
。表示当类型为String
并且字符串长度为5的时候,我们将这种case
标签称为守卫case
标签,将布尔表达式称为保护。
@Testpublic void switchCaseCaseTest() {String[] poems = { "千山鸟飞绝", "春城无处不飞花", "红豆生南国", "二月春风似剪刀","念奴娇" };for (String poem : poems) {switch (poem) {case null -> System.out.println("为空值");case String s when s.length() == 5 -> System.out.printf("五言绝句:%s%n", s);case String s when s.length() == 7 -> System.out.printf("七言绝句:%s%n", s);case String s -> System.out.printf("不知道是啥:%s%n", s); //剩余情况,仍然走这个}}}
使用守卫标签,我们可以编写更灵活和表达性强的代码。
2、JEP 400:默认UTF-8编码
JEP 400
在Java 18之前,Java的标准字符集(Charset)是根据操作系统的区域设置决定的。这意味着在不同的操作系统和区域设置下,Java程序的行为可能会不同,特别是在处理文本数据时。这种不一致性导致了许多问题,尤其是在跨平台部署和国际化应用程序时。而且 UTF-8 支持几乎所有语言的字符。所以 Java 18 将UTF-8设为Java平台的默认字符集,解决了跨平台的一致性问题。从此以后,Java 在处理文本数据时的行为在各种平台上就更加一致,减少了与字符编码相关的错误和混淆。
3、JEP 408:简易Web服务器
JEP 408
Java 18 引入该特性的主要目的为开发者提供一个轻量级、简单易用的 HTTP 服务器,用于原型制作、测试和开发环境。
Java 18 提供了一个命令:jwebserver
。利用这个命令,我们可以启动一个简单的 、最小化的静态 Web 服务器,但是它不支持 CGI 和 Servlet,所以最好的使用场景是用来测试、教育以及演示等需求。
关于jwebserver
需要注意:
-
构建目的是应用于测试与教学,不是为了替代
Jetty
、Nginx
等高级服务器 -
不提供身份验证、访问控制或加密等安全功能
-
仅支持
HTTP/1.1
,不支持HTTPS
-
仅支持
GET
、HEAD
请求 -
可以通过命令行、Java类启动
jwebserver的参数
示例1
准备页面
编写一个简单的页面
<html><head><meta charset="utf-8"></head></body><h1>天地玄黄,宇宙洪荒</h1></body></html>
启动
在页面所在的目录,我这里是mytest
中,使用命令行启动。
打开终端,输入jwebserver
命令:我这里路径没有设置到Path
里,所以使用全路径访问。如果设置了Path
,直接使用入jwebserver
即可。
访问控制台提示的路径http://127.0.0.1:8000/
,就可以看到内容了。
访问时可以看到终端输出日志
127.0.0.1 - - [27/2月/2024:18:53:19 +0800] "GET / HTTP/1.1" 200 -127.0.0.1 - - [27/2月/2024:18:53:20 +0800] "GET /favicon.ico HTTP/1.1" 404 -
使用java启动
public class JWebServerTest {public static void main(String[] args) {System.out.println("服务器启动"); // System.out.println(Paths.get("").toAbsolutePath());var addr = new InetSocketAddress(8001);// 路径需要使用绝对路径var server = SimpleFileServer.createFileServer(addr, Path.of(Paths.get("").toAbsolutePath().toString()),SimpleFileServer.OutputLevel.INFO);server.start();} }
如果使用jwebserver
来实现,实际上就是下面的命令:路径根据直接实际情况调整
jwebserver -p 8001 -d F:\workspacenew2023-04\mylab\mytest[目录的绝对路径] -o info
4、JEP 413:支持在 Java API 文档中加入代码片段
JEP 413 (tools/javadoc(tool)
)
在 Javadoc
工具中引入了 @snippet
标签,允许文档作者在 API 文档中嵌入源代码片段。
在之前的 Java 文档中,代码示例通常是文本形式,容易过时且难以验证正确性。通过这个特性,我们可以确保示例代码的准确性和时效性。
而且,我们还可以设置高亮(按字符串、按正则)、替换代码片段、源代码引用、链接到 API 文档的其他部分、甚至是自定义的 CSS 样式,使文档更加生动和易于阅读。
在Java 18之前,已经有一个@code
标签,可以用于在JavaDoc中编写小段的代码内容,@snipppet
对其进行了增强。具体可以参考JEP。
5、JEP 416:用方法句柄重新实现核心反射
JEP 416 (core-libs/java.lang:reflect
)
JEP416用方法句柄重新实现了核心反射。使用方法句柄代替 Java 语言的反射机制中的原生方法,降低java.lang.reflect
和java.lang.invoke
API的维护和开发成本。部分功能可能受影响。可能出现的问题包括:
-
检查内部生成的反射类(如
MagicAccessorImpl
的子类)的代码不再工作,必须更新。 -
试图破坏封装并将
Method
、field
和Constructor
类的私有最终修饰符字段的值更改为与基础成员不同的代码可能会导致运行时错误。必须更新此类代码。
为了降低这种兼容性风险,可以使用-Djdk.reflect.useDirectMethodHandle=false
来启用旧的实现作为解决方法。我们将在未来的版本中删除旧的核心反射实现。-Djdk.reflect.useDirectMethodHandle=false
的变通方法将在此时停止工作。
降低了对未来语言功能的反射支持的维护和开发成本,并且简化了Hotspot VM
代码。
6、JEP 417:向量 API(第三次孵化)
JEP 417 (core-libs
)
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 16 | 第一次孵化 | JEP 338 | 提供一个平台无关的方式来表达向量计算,能够充分利用现代处理器上的向量硬件指令。 |
Java 17 | 第二次孵化 | JEP 414 | 改进 |
Java 18 | 第三次孵化 | JEP 417 | 进一步增强 |
向量 API 是在 Java 16 中作为孵化特性引入的,Java 17 经历第二次孵化,Java 18 再一次孵化。
引入API来表示向量计算,这些向量计算在运行时可靠地编译为支持的CPU架构上的最佳向量指令,从而实现优于等效标量计算的性能。Java 18 对向量 API 进行了进一步的改进和增强,以更好地利用硬件功能,提高 Java 在数值计算和机器学习等领域的性能。
7、JEP 418:互联网地址解析 SPI
JEP 418 (core-libs/java.net
)
引入一个用于主机名称和地址解析的服务提供者接口(SPI),从而实现java.net.InetAddress
可以使用平台内置解析器以外的解析器。这种新的SPI
允许替换操作系统的本地解析程序,该解析程序通常配置为使用本地主机文件和域名系统(DNS)的组合。
8、JEP 419:外部函数和内存 API(第二次孵化)
JEP 419 (core-libs
)
孵化阶段。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 孵化特性 | JEP 370 | 引入了外部内存访问 API作为孵化特性 |
Java 15 | 第二次孵化 | JEP 383 | 优化外部内存访问 API |
Java 16 | 孵化特性 | JEP 389 | 引入了外部链接器 API |
Java 16 | 第三次孵化 | JEP 393 | 功能优化 |
Java 17 | 孵化特性 | JEP 412 | 引入了外部函数和内存 API |
Java 18 | 第二次孵化 | JEP 419 | 改进 |
外部函数和内存 API 是在 Java 17 中作为孵化器引入的,它提供对本机代码的静态类型的纯Java访问,其主要目的是改善 Java 与本地代码(如 C 或 C++)的互操作性。此API
与Foreign-Memory API
(JEP 393)一起,将大大简化绑定到本机库的错误处理过程。
一般Java想要调用本地代码需要使用Java Native Interface (JNI)
,但是JNI
操作比较复杂而且性能有限。
外部函数和内存API
提供了一套更简洁的API
,用于调用本地函数和处理本地内存,降低了复杂性,而且还设计了更多的安全保护措施,降低了内存泄露和应用崩溃的风险。
主要是通过两个组件实现的:
-
Foreign Function Interface (FFI): 允许 Java 代码直接调用非 Java 代码,比如 C/C++ 代码。
-
Foreign Memory Access API:提供了一种安全的方法来访问不受
JVM
管理的内存。
9、JEP 421:弃用 Finalization 功能
JEP 421
Finalization
是 Java 早期版本引入的功能,用于在对象被垃圾收集器销毁之前执行清理操作。然而,这个机制存在诸多问题,比如性能不佳、行为不可预测、容易导致内存泄漏等。
Java 18 将该功能标记为废弃,在将来的版本中移除。 finalize()
将不建议再被使用。目前默认情况下,Finalization
仍处于启用状态,但可以禁用以便于早期测试。在将来的版本中,默认情况下它将被禁用,在以后的版本中它将被删除。使用了Finalization
功能的库和应用程序的维护人员应该考虑迁移到其他资源管理技术,如try-with-resources statement 和 cleaners。
10、移除的APIs、工具、容器
参考:
-
Java SE 18中移除的API
-
Java SE 18中移除的工具和容器