Introduction to new features in Java 18

The article is continuously updated, you can follow the official account programmer Alang or visit the unread code blog .
This article has been included on Github.com/niumoo/Java… Welcome to Star.

Java 18 was officially released on March 22, 2022. Java 18 is not a long-term support version. This update brings a total of 9 new features.

OpenJDK Java 18 download: jdk.java.net/18/

OpenJDK Java 18 Documentation: openjdk.java.net/projects/jd…

give describe
JEP 400 Default is UTF-8
JEP 408 simple web server
JEP 413 Code snippets from the Java API documentation
JEP 416 Reimplement core reflection using method handles
JEP 417 Vector API (three incubations)
JEP 418 Internet Address Resolution SPI
JEP 419 Foreign Function & Memory API (second incubation)
JEP 420 switch pattern matching (secondary preview)
JEP 421 Deprecated complete removal

JEP 400: Default UTF-8 Character Encoding

JDK has always supported UTF-8 character encoding. This time, UTF-8 is set as the default encoding, that is, without any specification, all JDK APIs that require encoding use UTF-8 encoding by default. In this way, coding problems caused by different systems, different regions, and different environments can be avoided.

Mac OS 默认使用 UTF-8 作为默认编码,但是其他操作系统上,编码可能取决于系统的配置或者所在区域的设置。如中国大陆的 windows 使用 GBK 作为默认编码。很多同学初学 Java 时可能都遇到过一个正常编写 Java 类,在 windows 系统的命令控制台中运行却出现乱码的情况。

使用下面的命令可以输出 JDK 的当前编码。

# Mac 系统,默认 UTF-8
➜  ~ java -XshowSettings:properties -version 2>&1 | grep file.encoding
    file.encoding = UTF-8
    file.encoding.pkg = sun.io
➜  ~
复制代码

下面编写一个简单的 Java 程序,输出默认字符编码,然后输出中文汉字 ”你好“,看看 Java 18 和 Java 17 运行区别。

系统环境:Windows 11

import java.nio.charset.Charset;

public class Hello{
    public static void main(String[] args) {
        System.out.println(Charset.defaultCharset());
        System.out.println("你好");
    }
}
复制代码

从下面的运行结果中可以看到,使用 JDK 17 运行输出的默认字符编码是 GBK,输出的中文 ”你好“ 已经乱码了;乱码是因为 VsCode 默认的文本编辑器编码是 UTF-8,而中国地区的 Windows 11 默认字符编码是 GBK,也是 JDK 17 默认获取到的编码,所以会在控制台输出时乱码;而使用 JDK 18 输出的默认编码就是 UTF-8,所以可以正常的输出中文 ”你好“。

JEP 408:简单的 Web服务器

在 Java 18 中,提供了一个新命令 jwebserver,运行这个命令可以启动一个简单的 、最小化的静态Web 服务器,它不支持 CGI 和 Servlet,所以最好的使用场景是用来测试、教育、演示等需求。

其实在如 Python、Ruby、PHP、Erlang 等许多平台都提供了开箱即用的 Web 服务器,可见一个简单的Web 服务器是一个常见的需求,Java 一直没有这方面的支持,现在可以了。

在 Java 18 中,使用 jwebserver 启动一个 Web 服务器,默认发布的是当前目录。

在当前目录创建一个网页文件 index.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>标题</h1>
</body>
</html>
复制代码

启动 jwebserver.

➜  bin ./jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /Users/darcy/develop/jdk-18.jdk/Contents/Home/bin and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/
复制代码

浏览器访问:

browser access test

有请求时会在控制台输出请求信息:

127.0.0.1 - - [26/3月/2022:16:53:30 +0800] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [26/3月/2022:16:55:13 +0800] "GET / HTTP/1.1" 200 -
复制代码

通过 help 参数可以查看 jwebserver 支持的参数。

➜  bin ./jwebserver --help
Usage: jwebserver [-b bind address] [-p port] [-d directory]
                  [-o none|info|verbose] [-h to show options]
                  [-version to show version information]
Options:
-b, --bind-address    - 绑定地址. Default: 127.0.0.1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - 指定目录. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - 绑定端口. Default: 8000.
-h, -?, --help        - Prints this help message and exits.
-version, --version   - Prints version information and exits.
To stop the server, press Ctrl + C.
复制代码

JEP 413:Javadoc 中支持代码片段

在 Java 18 之前,已经支持在 Javadoc 中引入代码片段,这样可以在某些场景下更好的展示描述信息,但是之前的支持功能有限,比如我想高亮代码片段中的某一段代码是无能为力的。现在 Java 18 优化了这个问题,增加了 @snippet 来引入更高级的代码片段。

在 Java 18 之前,使用 <pre>{@code ...}</pre> 来引入代码片段。

 /**
  * 时间工具类
  * Java 18 之前引入代码片段:
  * <pre>{@code
  *     public static String timeStamp() {
  *        long time = System.currentTimeMillis();
  *         return String.valueOf(time / 1000);
  *     }
  * }</pre>
  *
  */
复制代码

生成 Javadoc 之后,效果如下:

Javadoc code snippet

高亮代码片段

从 Java 18 开始,可以使用 @snippet 来生成注释,且可以高亮某个代码片段。

/**
 * 在 Java 18 之后可以使用新的方式
 * 下面的代码演示如何使用 {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 *
 * 高亮显示 println
 *
 * {@snippet :
 * class HelloWorld {
 *     public static void main(String... args) {
 *         System.out.println("Hello World!");      // @highlight substring="println"
 *     }
 * }
 * }
 *
 */
复制代码

效果如下,更直观,效果更好。

Java 18 Javadoc

正则高亮代码片段

甚至可以使用正则来高亮某一段中的某些关键词:

/** 
  * 正则高亮:
  * {@snippet :
  *   public static void main(String... args) {
  *       for (var arg : args) {                 // @highlight region regex = "\barg\b"
  *           if (!arg.isBlank()) {
  *               System.out.println(arg);
  *           }
  *       }                                      // @end
  *   }
  *   }
  */
复制代码

生成的 Javadoc 效果如下:

替换代码片段

可以使用正则表达式来替换某一段代码。

 /** 
   * 正则替换:
   * {@snippet :
   * class HelloWorld {
   *     public static void main(String... args) {
   *         System.out.println("Hello World!");  // @replace regex='".*"' replacement="..."
   *     }
   * }
   * }
   */
复制代码

这段注释会生成如下 Javadoc 效果。

class HelloWorld {
    public static void main(String... args) {
        System.out.println(...);
    }
}
复制代码

附:Javadoc 生成方式

# 使用 javadoc 命令生成 Javadoc 文档
➜  bin ./javadoc -public -sourcepath ./src -subpackages com -encoding utf-8 -charset utf-8 -d ./javadocout
# 使用 Java 18 的 jwebserver 把生成的 Javadoc 发布测试
➜  bin ./jwebserver -d /Users/darcy/develop/javadocout
复制代码

访问测试:

JEP 416:使用方法句柄重新实现反射核心功能

Java 18 改进了 java.lang.reflect.MethodConstructor 的实现逻辑,使之性能更好,速度更快。这项改动不会改动相关 API ,这意味着开发中不需要改动反射相关代码,就可以体验到性能更好反射。

OpenJDK 官方给出了新老实现的反射性能基准测试结果。

Java 18 之前:

Benchmark                                     Mode  Cnt   Score  Error  Units
ReflectionSpeedBenchmark.constructorConst     avgt   10  68.049 ± 0.872  ns/op
ReflectionSpeedBenchmark.constructorPoly      avgt   10  94.132 ± 1.805  ns/op
ReflectionSpeedBenchmark.constructorVar       avgt   10  64.543 ± 0.799  ns/op
ReflectionSpeedBenchmark.instanceFieldConst   avgt   10  35.361 ± 0.492  ns/op
ReflectionSpeedBenchmark.instanceFieldPoly    avgt   10  67.089 ± 3.288  ns/op
ReflectionSpeedBenchmark.instanceFieldVar     avgt   10  35.745 ± 0.554  ns/op
ReflectionSpeedBenchmark.instanceMethodConst  avgt   10  77.925 ± 2.026  ns/op
ReflectionSpeedBenchmark.instanceMethodPoly   avgt   10  96.094 ± 2.269  ns/op
ReflectionSpeedBenchmark.instanceMethodVar    avgt   10  80.002 ± 4.267  ns/op
ReflectionSpeedBenchmark.staticFieldConst     avgt   10  33.442 ± 2.659  ns/op
ReflectionSpeedBenchmark.staticFieldPoly      avgt   10  51.918 ± 1.522  ns/op
ReflectionSpeedBenchmark.staticFieldVar       avgt   10  33.967 ± 0.451  ns/op
ReflectionSpeedBenchmark.staticMethodConst    avgt   10  75.380 ± 1.660  ns/op
ReflectionSpeedBenchmark.staticMethodPoly     avgt   10  93.553 ± 1.037  ns/op
ReflectionSpeedBenchmark.staticMethodVar      avgt   10  76.728 ± 1.614  ns/op
复制代码

Java 18 的新实现:

Benchmark                                     Mode  Cnt    Score   Error  Units
ReflectionSpeedBenchmark.constructorConst     avgt   10   32.392 ± 0.473  ns/op
ReflectionSpeedBenchmark.constructorPoly      avgt   10  113.947 ± 1.205  ns/op
ReflectionSpeedBenchmark.constructorVar       avgt   10   76.885 ± 1.128  ns/op
ReflectionSpeedBenchmark.instanceFieldConst   avgt   10   18.569 ± 0.161  ns/op
ReflectionSpeedBenchmark.instanceFieldPoly    avgt   10   98.671 ± 2.015  ns/op
ReflectionSpeedBenchmark.instanceFieldVar     avgt   10   54.193 ± 3.510  ns/op
ReflectionSpeedBenchmark.instanceMethodConst  avgt   10   33.421 ± 0.406  ns/op
ReflectionSpeedBenchmark.instanceMethodPoly   avgt   10  109.129 ± 1.959  ns/op
ReflectionSpeedBenchmark.instanceMethodVar    avgt   10   90.420 ± 2.187  ns/op
ReflectionSpeedBenchmark.staticFieldConst     avgt   10   19.080 ± 0.179  ns/op
ReflectionSpeedBenchmark.staticFieldPoly      avgt   10   92.130 ± 2.729  ns/op
ReflectionSpeedBenchmark.staticFieldVar       avgt   10   53.899 ± 1.051  ns/op
ReflectionSpeedBenchmark.staticMethodConst    avgt   10   35.907 ± 0.456  ns/op
ReflectionSpeedBenchmark.staticMethodPoly     avgt   10  102.895 ± 1.604  ns/op
ReflectionSpeedBenchmark.staticMethodVar      avgt   10   82.123 ± 0.629  ns/op
复制代码

可以看到在某些场景下性能稍微好些。

JEP 417:Vector API(三次孵化)

在 Java 16 中引入一个新的 API 来进行向量计算,它可以在运行时可靠的编译为支持的 CPU 架构,从而实现更优的计算能力。

在 Java 17 中改进了 Vector API 性能,增强了例如对字符的操作、字节向量与布尔数组之间的相互转换等功能。

现在在 JDK 18 中将继续优化其性能。

JEP 418:互联网地址解析 SPI

对于互联网地址解析 SPI,为主机地址和域名地址解析定义一个 SPI,以便java.net.InetAddress可以使用平台内置解析器以外的解析器。

InetAddress inetAddress = InetAddress.getByName("www.wdbyte.com");
System.out.println(inetAddress.getHostAddress());
// 输出
// 106.14.229.49
复制代码

JEP 419:Foreign Function & Memory API (第二次孵化)

新的 API 允许 Java 开发者与 JVM 之外的代码和数据进行交互,通过调用外部函数,可以在不使用 JNI 的情况下调用本地库。

这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign来编译和运行 Java 代码,Java 18 改进了相关 API ,使之更加简单易用。

历史

JEP 420:switch 表达式(二次孵化)

从 Java 17 开始,对于 Switch 的改进就已经在进行了,Java 17 的 JEP 406 已经对 Switch 表达式进行了增强,使之可以减少代码量。

下面是几个例子:

// JDK 17 以前
static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}
复制代码

After Java 17, it can be improved by writing the following:

// JDK 17 之后
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        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        -> o.toString();
    };
}
复制代码

switch can nullbe combined with to judge:

static void testFooBar(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}
复制代码

Complex expressions can be added to case:

static void testTriangle(Shape s) {
    switch (s) {
        case Triangle t && (t.calculateArea() > 100) ->
            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    }
}
复制代码

Type judgment can be performed in case:

sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {}  // Implicitly final

static int testSealedExhaustive(S s) {
    return switch (s) {
        case A a -> 1;
        case B b -> 2;
        case C c -> 3;
    };
}
复制代码

Extension: JEP 406: Type Matching for Switch (Preview)

JEP 421: Deprecation Removal Related

Finalization will be removed in the future, currently Finalization is still enabled by default, but can be disabled manually; in future releases, it will be disabled by default; in future releases, it will be removed. Need to do resource management can try try-with-resourcesor java.lang.ref.Cleaner.

refer to

subscription

You can search for the programmer Alang on WeChat or visit the unread code blog to read.
This article has been included on Github.com/niumoo/Java… Welcome to Star.

Guess you like

Origin juejin.im/post/7085161493376794631