• Java 18 新功能介绍


    文章持续更新,可以关注公众号程序猿阿朗或访问未读代码博客
    本文 Github.com/niumoo/JavaNotes 已经收录,欢迎Star。
    Java 18 在2022 年 3 月 22 日正式发布,Java 18 不是一个长期支持版本,这次更新共带来 9 个新功能。

    OpenJDK Java 18 下载:https://jdk.java.net/18/

    OpenJDK Java 18 文档:https://openjdk.java.net/projects/jdk/18/

    JEP 描述
    JEP 400 默认为 UTF-8
    JEP 408 简单的网络服务器
    JEP 413 Java API 文档中的代码片段
    JEP 416 使用方法句柄重新实现核心反射
    JEP 417 Vector API(三次孵化)
    JEP 418 互联网地址解析 SPI
    JEP 419 Foreign Function & Memory API (二次孵化)
    JEP 420 switch 模式匹配(二次预览)
    JEP 421 弃用完成删除

    JEP 400:默认 UTF-8 字符编码

    JDK 一直都是支持 UTF-8 字符编码,这次是把 UTF-8 设置为了默认编码,也就是在不加任何指定的情况下,默认所有需要用到编码的 JDK API 都使用 UTF-8 编码,这样就可以避免因为不同系统,不同地区,不同环境之间产生的编码问题。

    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/
    

    浏览器访问:

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

    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 之后,效果如下:

    高亮代码片段

    从 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"
     *     }
     * }
     * }
     *
     */
    

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

    正则高亮代码片段

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

    /** 
      * 正则高亮:
      * {@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/opReflectionSpeedBenchmark.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/opReflectionSpeedBenchmark.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/opReflectionSpeedBenchmark.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/opReflectionSpeedBenchmark.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;
    }
    

    而在 Java 17 之后,可以通过下面的写法进行改进:

    // 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 可以和 null 进行结合判断:

    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");
        }
    }

    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");
        }
    }

    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;
        };
    }
    

    扩展:JEP 406:Switch 的类型匹配(预览)

    JEP 421:弃用删除相关

    在未来将删除 Finalization,目前 Finalization 仍默认保持启用状态,但是已经可以手动禁用;在未来的版本中,将会默认禁用;在以后的版本中,它将被删除。需要进行资源管理可以尝试 try-with-resources 或者 java.lang.ref.Cleaner

    参考

    订阅

    可以微信搜一搜程序猿阿朗或访问未读代码博客阅读。
    本文 Github.com/niumoo/JavaNotes 已经收录,欢迎Star。

  • 相关阅读:
    WCF 第二章 契约 异步访问服务操作
    WCF 第一章 基础 在IIS中寄宿服务
    哈希表 解释 和 实现
    ACM2010省赛总结
    hashTable实现
    c# winform 应用编程代码总结 14
    徽文化让世博更多彩
    c# winform 应用编程代码总结 15
    Socket用法详解
    IE插件技术 BHO C# IE 插件
  • 原文地址:https://www.cnblogs.com/niumoo/p/16128775.html
Copyright © 2020-2023  润新知