侧边栏壁纸
博主头像
搞钱拒绝ICU

行动起来,活在当下

  • 累计撰写 27 篇文章
  • 累计创建 9 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

JDK9-JDK17新特性与示例

admin
2025-07-01 / 0 评论 / 0 点赞 / 6 阅读 / 0 字

一、JDK9

1.1 说明

2017年9月发布,从Java 9 这个版本开始,Java 的计划发布周期是 6 个月,这意味着Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的 (6 个月为周期)发布模式,并逐步的将 Oracle JDK 原商业特性进行开源。针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(LTS)。

JDK9非LTS版本

1.2 新特性

1.2.1 模块化系统

​ 说明:

​ 模块就是代码和数据的封装体。模块的代码被组织成多个包,每个包中包含Java类和接口;模块的数据则包括资源文件和其他静态信息。Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。

1.2.2 jShell命令

​ 说明:

​ JShell 是一个交互式的编程环境工具。它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接输入表达式并查看其执行结果。

1.2.3 接口的私有方法

在 JDK 9 中,一个接口中能定义如下几种变量/方法:

  • 常量

  • 抽象方法

  • 默认方法

  • 静态方法

  • 私有方法

  • 私有静态方法

1.2.4 try-with-resources改进

​ 说明:

​ 如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

​ 示例:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
 
public class Tester {
   public static void main(String[] args) throws IOException {
      System.out.println(readData("test"));
   } 
   static String readData(String message) throws IOException {
      Reader inputString = new StringReader(message);
      BufferedReader br = new BufferedReader(inputString);
      try (br) {
         return br.readLine();
      }
   }
}

1.2.5 String存储结构变更

​ 说明:

​ 在以前的版本中,String一直是用char[]存储,但是在Java9中,String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约 了一些空间。

​ 部分源码展示:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    /**
     * The value is used for character storage.
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     *
     * Additionally, it is marked with {@link Stable} to trust the contents
     * of the array. No other facility in JDK provides this functionality (yet).
     * {@link Stable} is safe here, because value is never null.
     */
    @Stable
    private final byte[] value;

    /**
     * The identifier of the encoding used to encode the bytes in
     * {@code value}. The supported values in this implementation are
     *
     * LATIN1
     * UTF16
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     */
    private final byte coder;
}

1.2.6 快速创建只读集合

​ 说明:

​ List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。

​ 示例:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;
 
public class Tester {
 
   public static void main(String []args) {
      Set<String> set = Set.of("A", "B", "C");      
      System.out.println(set);
      List<String> list = List.of("A", "B", "C");
      System.out.println(list);
      Map<String, String> map = Map.of("A","Apple","B","Boy","C","Cat");
      System.out.println(map);
  
      Map<String, String> map1 = Map.ofEntries (
         new AbstractMap.SimpleEntry<>("A","Apple"),
         new AbstractMap.SimpleEntry<>("B","Boy"),
         new AbstractMap.SimpleEntry<>("C","Cat"));
      System.out.println(map1);
   }
}

1.2.7 InputStream tranferTo()

​ 说明:

​ transferTo,可以用来将数据直接 传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法。

​ 示例:

//java9新特性九:InputStream的新方法:tranferTo()
    public void test() {
        ClassLoader cl = this.getClass().getClassLoader();
        try (InputStream is = cl.getResourceAsStream("hello.txt");
             OutputStream os = new FileOutputStream("src\\hello1.txt")) {
            is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.2.8 Stream API的加强

​ 说明:

​ Stream 接口中添加了 4 个新的方法: takeWhile, dropWhile, ofNullable,还有个 iterate 方法的新重载方法,可以 让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

​ 除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。 现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个 (可能是空的) Stream 对象。

​ 示例:

// takeWhile 返回从开头开始的按照指定规则尽量多的元素
List<Integer> list = List.of(45, 43, 76, 87, 42, 77, 90, 73, 67, 88);
list.stream().takeWhile(x -> x < 50).forEach(System.out::println);

list = List.of(1, 2, 3, 4, 5, 6, 7, 8);
list.stream().takeWhile(x -> x < 5).forEach(System.out::println);

// dropWhile():与 takeWhile 相反,返回剩余的元素
list = List.of(23, 43, 45, 55, 61, 54, 32, 2, 45, 89, 7);
list.stream().dropWhile(x -> x < 60).forEach(System.out::println);

// ofNullable 方 法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream
Stream<Integer> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());

// iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什 么时候结束迭代
// 原来的控制终止方式:
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println);
// 现在的终止方式:
Stream.iterate(1, i -> i <= 10, i -> i + 1).forEach(System.out::println);

// Optional类中stream()的使用
Optional<List<Integer>> optional = Optional.ofNullable(list);
Stream<List<Integer>> stream2 = optional.stream();
stream2.flatMap(Collection::stream).forEach(System.out::println);

1.2.9 多版本兼容Jar包

​ 说明:

​ 多版本兼容 JAR 功能能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。

1.2.10 设置G1为JVM默认垃圾收集器

​ 说明:​ 默认垃圾收集器改为G1。

二、JDK10

2.1 说明

2018年3月发布,一共定义了109个新特性,其中包含12个JEP,还有一些新API和JVM规范以及JAVA语言规范上 的改动。

JDK10非LTS版本

2.2 新特性

2.2.1 局部变量类型推断

​ 说明:

​ JDK10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量;它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其他类型的变量声明。标识符var不是关键字;相反,它是一个保留的类型名称。这意味着var用作变量,方法名或则包名称的代码不会受到影响;但var不能作为类或则接口的名字(但这样命名是比较罕见的,因为他违反了通常的命名约定,类和接口首字母应该大写)。

​ 使用场景:

// 1.局部变量的初始化
var list = new ArrayList<>();

// 2.增强for循环中的索引
for (var v : list) {
  System.out.println(v);
}

// 3.传统for循环中
for (var i = 0; i < 100; i++) {
  System.out.println(i);
}

// 接收多接口实现,可以调用各个接口的方法
public class Main {
    public static void main(String[] args) {
        var test = getInstance("Test1");
        test.method1();
        test.method2();
        test.method3();
    }

    public static <T extends Interface1 & Interface2 & Interface3> T getInstance(String name) {
        if ("Test1".equals(name)) {
            return (T) new Test1();
        } else if ("Test2".equals(name)) {
            return (T) new Test2();
        }
        return null;
    }
}

​ 不适用的场景:

  • 没有初始化的局部变量声明

  • 方法的返回类型

  • 方法的参数类型

  • 构造器的参数类型

  • 属性

  • catch块

  • 初始值为null

  • 接收方法引用

  • 接收lambda表达式

2.2.2 不可变集合改进

​ 说明:​ List,Set,Map 提供了静态方法copyOf()返回入参集合的一个不可变拷贝。使用 copyOf() 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

static <E> List<E> copyOf(Collection<? extends E> coll) {
    return ImmutableCollections.listCopy(coll);
}

java.util.stream.Collectors 中新增了静态方法,用于将流中的元素收集为不可变的集合。

var list = new ArrayList<>();
list.stream().collect(Collectors.toUnmodifiableList());
list.stream().collect(Collectors.toUnmodifiableSet());

2.2.3 应用程序类数据共享

​ 说明:​ JVM 启动时有一步是需要在内存中加载类,而如果有多个 jar,加载第一个 jar 的速度是最慢的。这就延长了程序的启动时间,为了减少这个时间,Java 10 引入了应用程序类数据共享(CDS)机制,它可以把你想共享的类共享在程序之间,使不同的 Java 进程之间共享这个类来减少这个类占用的空间以及加载速度。

2.2.4 G1优化

​ 说明:

​ JDK10通过并行FullGC,改善G1的延迟。G1垃圾收集器在JDK9中是默认的。以前的默认值并行收集器中有一个并行的FullGC。为了尽量减少对使用GC用户的影响,G1的FullGC也应该并行。

2.2.5 ROOT证书

​ 说明:​ 在JDK中提供一组默认的root认证权威(CA)证书。在Oracle的JavaSE根CA程序中开源root证书,以使OpenJDK构建对开发人员更有吸引力,并减少这些构建和OracleJDK构建之间的差异。cacerts密钥存储库是JDK的一部分,它的目的是包含一组root证书,这些root证书可以用来在各种安全协议中使用的证书链中建立信任。

2.2.6 对容器支持改进

​ 说明:​ JVM 已被修改为知道它正在 Docker 容器中运行,并将提取容器特定的配置信息,而不是查询操作系统。提取的信息是已分配给容器的 CPU 数量和总内存。Java 进程可用的 CPU 总数是根据任何指定的 cpu 集、cpu 份额或 cpu 配额计算得出的。此支持仅在基于 Linux 的平台上可用。这个新的支持默认启用,可以在命令行中使用 JVM 选项禁用:

-XX:-UseContainerSupport

此外,此更改添加了一个 JVM 选项,可以指定 JVM 将使用的 CPU 数量:

-XX:ActiveProcessorCount=count

此计数会覆盖 JVM 中的任何其他自动 CPU 检测逻辑。

添加了三个新的 JVM 选项,允许 Docker 容器用户对用于 Java 堆的系统内存量进行更细粒度的控制:

-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

三、JDK11

3.1 说明

​ JDK 11 于 2018 年 9 月 25 日正式发布。这是 Java 大版本周期变化后的第一个长期支持版本,非常值得关注。最新发布的 Java11 将带来 ZGC、 Http Client 等重要特性,一共包 含 17 个 JEP。

​ JDK11是LTS版本

3.2 新特性

3.2.1 新增字符串处理方法

方法

描述

isBlank()

判断字符串是否为空白

strip()

去除首尾空白

stripTrailing()

去除尾部空格

stripLeading()

去除首部空格

repeat()

复制字符串

lines().count()

行数统计

3.2.2 HttpClient

​ 说明:

​ 长期以来,如果要访问Http资源,JDK的标准库中只有一个HttpURLConnection,这个古老的API使用非常麻烦,而且已经不适用于最新的HTTP协议。JDK11中新的HttpClient支持HTTP/2和WebSocket,并且可以使用异步接口。

package com.itranswarp.jdk11;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

public class HttpApi {

    public static void main(String[] args) {
        HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://www.qq.com/")).GET().build();
        HttpResponse.BodyHandler<String> bodyHandler = HttpResponse.BodyHandlers.ofString();
        HttpClient client = HttpClient.newHttpClient();
        CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, bodyHandler);
        future.thenApply(HttpResponse::body).thenAccept(System.out::println).join();
    }
}

3.2.3 ZGC

​ 说明:​ ZGC 即 Z Garbage Collector(垃圾收集器或垃圾回收器),这应该是 Java 11 中最为瞩目的特性,没有之一。GC暂停时间不会超过10ms,既能处理几百兆的小堆, 也能处理几个T的大堆(OMG),和G1相比, 应用吞吐能力不会下降超过15%,为未来的GC功能和利用colord指针以及Load barriers优化奠定基础,初始只支持64位系统。ZGC 是一个可伸缩的、低延迟的垃圾收集器,主要为了满足如下目标进行设计:

  • GC 停顿时间不超过 10ms

  • 即能处理几百 MB 的小堆,也能处理几个 TB 的大堆

  • 应用吞吐能力不会下降超过 15%(与 G1 回收算法相比)

  • 方便在此基础上引入新的 GC 特性和利用 colord

  • 针以及 Load barriers 优化奠定基础

  • 当前只支持 Linux/x64 位平台 停顿时间在 10ms 以下,10ms 其实是一个很保守的数据,即便是 10ms 这个数据,也是 GC 调优几乎达不到的极值。根据 SPECjbb 2015 的基准测试,128G 的大堆下最大停顿时间才 1.68ms,远低于 10ms,和 G1 算法相比,改进非常明显。

​ 使用方式:

// 配置JVM参数
-XX:+ UnlockExperimentalVMOptions -XX:+ UseZGC

3.2.4 支持 TLS 1.3 协议

​ 说明:

​ Java 11 中包含了传输层安全性(TLS)1.3 规范(RFC 8446)的实现,替换了之前版本中包含的 TLS,包括 TLS 1.2,同时还改进了其他 TLS 功能,例如 OCSP 装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627),在安全性和性能方面也做了很多提升。新版本中包含了 Java 安全套接字扩展(JSSE)提供 SSL,TLS 和 DTLS 协议的框架和 Java 实现。目前,JSSE API 和 JDK 实现支持 SSL 3.0,TLS 1.0,TLS 1.1,TLS 1.2,DTLS 1.0 和 DTLS 1.2。

3.2.5 可运行单一Java源文件

​ 说明:​ 增强 java启动器以运行作为单个 Java 源代码文件提供的程序,包括通过 “shebang”文件和相关技术在脚本内使用。

此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行。唯一的约束在于所有相关的类必须定义在同一个 Java 文件中。

java HelloWorld.java
// 之前版本
javac HelloWorld.java
java HelloWorld

3.2.6 Lambda参数的局部变量语法

​ 说明:​ 允许var在声明隐式类型 lambda 表达式的形式参数时使用,并且可以将注解应用于局部变量和 Lambda 表达式。

​ 示例:

var list = List.of(1, 2, 3, 4);
list.stream().forEach((@NotNull var a) -> System.out.println(a));

四、JDK12

4.1 说明

​ JDK 12 于 2019 年 3 月 19 日正式发布。

JDK12非LTS版本

4.2 新特性

4.2.1 扩展switch

​ 说明:​ switch不仅可以作为语句也可以作为表达式。 无论作为语句或者作为表达式,switch都可以使用传统/简化的作用域和控制流行为。 这将有助于简化代码,并为在switch中使用模式匹配铺平道路。

​ 示例:

// JDK12之前
public static String switchJava12Before(String day) {
    String season;
    switch (day) {
        case "march":
        case "april":
        case "may":
            season = "春天";
            break;
        case "june":
        case "july":
        case "august":
            season = "夏天";
            break;
        case "september":
        case "october":
        case "november":
            season = "秋天";
            break;
        case "december":
        case "january":
        case "february":
            season = "冬天";
            break;
      	default:
        		season = "error";
    }
    return season;
}

// JDK12
public static String switchJava12(String day) {
    return switch (day) {
        case "march", "april", "may" -> "春天";
        case "june", "july", "august" -> "夏天";
        case "september", "october", "november" -> "秋天";
        case "december", "january", "february" -> "冬天";
        default -> "error";
    };
}

4.2.2 Files.mismatch

​ 说明:

​ 对比两个文件,如果内容一致,会返回 -1 ,如果内容不同,会返回不同的字节开始位置。

​ 示例:

// 创建两个文件
Path pathA = Files.createFile(Paths.get("a.txt"));
Path pathB = Files.createFile(Paths.get("b.txt"));

// 写入相同内容
Files.writeString(pathA, "abc", StandardOpenOption.WRITE);
Files.writeString(pathB, "abc", StandardOpenOption.WRITE);
long mismatch = Files.mismatch(pathA, pathB);
System.out.println(mismatch);

// 追加不同内容
Files.writeString(pathA, "123", StandardOpenOption.APPEND);
Files.writeString(pathB, "124", StandardOpenOption.APPEND);
mismatch = Files.mismatch(pathA, pathB);
System.out.println(mismatch);
// 删除创建的文件
pathA.toFile().deleteOnExit();
pathB.toFile().deleteOnExit();
// result
// -1
// 5

4.2.3 Compact Number

​ 说明:​ NumberFormat增加了以紧凑格式格式化数字的支持。 紧凑的数字格式是指数字的简短形式或易于理解的形式。 例如,在en_US语言环境中,根据NumberFormat.Style指定的样式,可以将1000格式化为“ 1K”,将1000000格式化为“ 1M”。 紧凑数字格式由LDML的紧凑数字格式规范定义。

​ 示例:

NumberFormat nf = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
System.out.println(nf.format(100));
System.out.println(nf.format(1000));
System.out.println(nf.format(10000));
System.out.println(nf.format(100000));
System.out.println(nf.format(1000000));

// 设置小数位数
nf.setMaximumFractionDigits(1);
System.out.println(nf.format(1234));
System.out.println(nf.format(123456));
System.out.println(nf.format(12345678));
// 输出
// 100
// 1K
// 10K
// 100K
// 1M
// 1.2K
// 123.5K
// 12.3M

4.2.4 Shenandoah GC

​ 说明:​ 添加名为 Shenandoah 的新垃圾收集 (GC) 算法,该算法通过与正在运行的 Java 线程同时执行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关,这意味着无论您的堆是 200 MB 还是 200 GB,您都将具有相同的一致暂停时间。

​ 启用方式:

// 修改JVM参数
XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

4.2.5 G1优化

​ 说明:​ 增强G1垃圾收集器,以在空闲时自动将Java堆内存返回给操作系统。

4.2.6 ZGC 并发类卸载

​ 说明:​ ZGC现在支持类卸载,通过卸载不使用的类来释放这些类相关的数据结构,从而减少应用程序的总体占用空间。因为是并发执行,所以不会停止 Java 应用程序线程的执行,也因此对 GC 的暂停时间影响微乎其微。默认情况下启用此功能,但可以使用命令行选项禁用:

-XX:-ClassUnloading

五、JDK13

5.1 说明

​ JDK 13 于 2019 年 9 月 17 日正式发布。

JDK13非LTS版本

*** JDK13非LTS版本***

5.2 新特性

5.2.1 文本块升级

​ 说明:​ 文本块是一种多行字符串文字,它避免了大多数转义序列的需要,以一种可预测的方式自动设置字符串的格式,并在需要时使开发人员可以控制格式。在 Java 中,在字符串文字中嵌入 HTML、XML、SQL 或 JSON 的片段"..."通常需要在包含该片段的代码编译之前使用转义和连接进行大量编辑。它可以用于在字符串文字可能出现的任何地方表示字符串,但提供了更大的表达能力和更少的意外复杂性。 文本块由零个或多个内容字符组成,并由开始和结束定界符括起来。 开始定界符是三个双引号字符 ( ) 的序列,"""后跟零个或多个空格,后跟行终止符。内容从起始定界符的行终止符之后的第一个字符开始。 结束分隔符是三个双引号字符的序列。内容以结束分隔符的第一个双引号之前的最后一个字符结束。 与字符串文字中的字符不同,内容可以直接包含双引号字符。"允许在文本块中使用,但不是必需的或不建议使用。选择粗分隔符 ( """) 以便"字符可以显示为未转义,并且还可以在视觉上区分文本块和字符串文字。 与字符串文字中的字符不同,内容可以直接包含行终止符。\n允许在文本块中使用,但不是必需的或不建议使用。

​ HTML 示例:

// JDK13之前
String html = "<html>\n" +
                "    <body>\n" +
                "        <p>Hello, world</p>\n" +
                "    </body>\n" +
                "</html>\n";

// JDK13
String html = """
                <html>
                    <body>
                        <p>Hello, world</p>
                    </body>
                </html>
                """;

​ SQL示例:

// JDK13之前
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
                "WHERE `CITY` = 'INDIANAPOLIS'\n" +
                "ORDER BY `EMP_ID`, `LAST_NAME`;\n";
                
// JDK13
String query = """
                SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
                WHERE `CITY` = 'INDIANAPOLIS'
                ORDER BY `EMP_ID`, `LAST_NAME`;
                """;

​ 多语言语法示例:

// JDK13之前
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        Object obj = engine.eval("function hello() {\n" +
                "    print('\"Hello, world\"');\n" +
                "}\n" +
                "\n" +
                "hello();\n");

// JDK13
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        Object obj = engine.eval("""
                function hello() {
                    print('"Hello, world"');
                }
                                         
                hello();
                """);

5.2.2 ZGC优化

​ 说明: 增强 ZGC 以将未使用的堆内存返回给操作系统。ZGC 此前不会取消提交并将内存返回给操作系统,即使该内存已长期未使用也是如此。此行为并非对于所有类型的应用程序和环境都是最佳的,尤其是那些关注内存占用的应用程序和环境。

5.2.3 newFileSystem

​ 说明:

​ 添加了三种新方法,java.nio.file.FileSystems以便更轻松地使用将文件内容视为文件系统的文件系统提供程序。

  • newFileSystem(Path)

  • newFileSystem(Path, Map<String, ?>)

  • newFileSystem(Path, Map<String, ?>, ClassLoader)

newFileSystem(Path, Map<String, ?>)对于使用现有 2-arg 并将newFileSystem(Path, ClassLoader)类加载器指定为的代码,添加会产生源(但不是二进制)兼容性问题null.例如,无法编译以下内容,因为对的引用newFileSystem不明确:

FileSystem fs = FileSystems.newFileSystem(path, null);

为了避免不明确的引用,需要修改此代码以将第二个参数转换为java.lang.ClassLoader.

5.2.4 重新实现SocketAPI

​ 说明:

​ java.net.Socket和API的底层实现java.net.ServerSocket已在此版本中替换。JEP 353 提供了有关此更改的所有详细信息。我们已尽一切努力确保新实现与旧实现兼容,但在新旧实现行为不同的极端情况下,现有代码可能依赖于未指定的行为。JDK 继续包含旧的实现(称为“PlainSocketImpl”或“plain”实现)以允许此类代码继续运行。旧的实现是通过使用系统属性“ jdk.net.usePlainSockteImpl”集运行或设置为值“ true”来选择的,即使用-Djdk.net.usePlainSocketImpl或运行-Djdk.net.usePlainSocketImpl=true。该属性还可以在 JDK 网络配置文件(位于${java.home}/conf/net.properties. 旧的实现以及选择旧实现的系统属性将在未来的版本中删除。

六、JDK14

6.1 说明

​ DK 14于 2020 年 3 月 17 日全面发布。

JDK14非LTS版本

6.2 新特性

6.2.1 instanceof模式匹配

​ 说明:​ 通过运算 符的模式匹配增强 Java 编程语言instanceof 。模式匹配 允许更简洁、更安全地表达程序中的通用逻辑,即从对象中条件提取组件。这是JDK 14 中的预览语言功能。

在某些情况下,您不知道对象的确切类型。为了解决这个问题,Java有instanceof运算符,可用于针对不同类型进行测试。这样做的缺点是确定了对象的类型。如果要使用显式类型转换,则必须使用显式类型转换:

if (o instanceof String) {
  String s = (String)o;
  System.out.println(s.length);
}

JDK14:

if (o instanceof String s) {
  System.out.println(s.length);
}

变量的范围限于在逻辑上正确使用的地方,因此:

if (o instanceof String s) {
  System.out.println(s.length);
} else {
  // s在这里超出范围
}

该范围也可以在条件语句中应用,因此我们可以执行以下操作:

if (o instanceof String s && s.length() > 4) ...

6.2.2 Records

​ 说明:​ 通过记录增强 Java 编程语言。记录提供了一种紧凑的语法来声明类,这些类是浅层不可变数据的透明持有者。这是JDK 14 中的预览语言功能。

​ 您可以创建类来保存数据,并使用封装来控制如何访问和修改该数据。对象的使用使操作复杂的数据类型变得简单而直接。这是Java如此流行作为平台的原因之一。缺点(到现在为止)是,创建数据类型非常冗长,即使在最直接的情况下也需要大量代码。让我们看一下基本二维点所需的代码:

public class Point {
  private final double x;
  private final double y;

  public Point(double x, double y) {
    this.x = x;
    this.y = y;
  }

  public double getX() {
    return x;
  }

  public double getY() {
    return y;
  }
}

JDK14使用record方式:

public record Point(double x, double y) { }

允许显式规范构造函数仅执行其参数的验证和规范化,并省略明显的字段初始化。例如:

record Range(int lo, int hi) {
  public Range {
    if (lo > hi)  /* referring here to the implicit constructor parameters */
      throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
  }
}

​ 语法:

RecordDeclaration:
  {ClassModifier} record TypeIdentifier [TypeParameters] 
    (RecordComponents) [SuperInterfaces] [RecordBody]

RecordComponents:
  {RecordComponent {, RecordComponent}}

RecordComponent:
  {Annotation} UnannType Identifier

RecordBody:
  { {RecordBodyDeclaration} }

RecordBodyDeclaration:
  ClassBodyDeclaration
  RecordConstructorDeclaration

RecordConstructorDeclaration:
  {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName
    [Throws] ConstructorBody

​ 反射语法:

public static void main(String[] args) throws IOException, ScriptException {
    Point point = new Point(1, 2);
    RecordComponent[] recordComponents = point.getClass().getRecordComponents();
    System.out.println(recordComponents[0].getName());
}
public record Point(double x, double y) { }

6.2.3 删除CMS

​ 说明:

​ gc/cms此更改将禁用 CMS 编译,删除源树中目录的内容,并删除仅与 CMS 相关的选项。文档中对 CMS 的引用也将被清除。尝试使用 CMS 的测试将根据需要删除或调整。

尝试通过该选项使用 CMS-XX:+UseConcMarkSweepGC将导致出现以下警告消息:

Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; \
support was removed in <version>

并且虚拟机将使用默认收集器继续执行。

​ 替代方案:

​ CMS 的代码可以保存在存储库中,但不能编译。然而,如果没有维护者,代码很快就会过时,但可能会给人一种受到支持的错误印象。用户可以转移到 G1 垃圾收集器或任何其他收集器。只要早期版本仍支持 CMS,绝对需要 CMS 的用户仍可以使用它。

6.2.4 Helpful NullPointerExceptions

​ 说明:​ 通过精确描述哪个变量为空,提高JVM生成的NullPointerExceptions的可用性。

  • 向开发人员和支持人员提供有关程序提前终止的有用信息。

  • 通过更清晰地将动态异常与静态程序代码关联起来,提高程序理解。

  • 减少新开发人员经常对 s 产生的困惑和担忧NullPointerException

JDK14之前在简单的情况下,找出问题的原因很简单。如果我们尝试运行以下代码:

public class NullTest {
  List<String> list;
  public NullTest() {
    list.add("foo");
  }
}

生成的错误是:

Exception in thread "main" java.lang.NullPointerException
            at jdk14.NullTest.<init>(NullTest.java:16)
            at jdk14.Main.main(Main.java:15)

由于我们在第16行上引用列表,因此很明显,列表是罪魁祸首,我们可以快速解决问题。 但是,如果我们在这样的行中使用链接引用:

a.b.c.d = 12;

运行此命令时,我们可能会看到如下错误:

Exception in thread "main" java.lang.NullPointerException
    at Prog.main(Prog.java:5)

问题在于我们无法由此确定异常是由于a为null,b为null还是c为null的结果。我们要么需要使用IDE中的调试器,要么更改代码,将引用分隔到不同的行上。两者都不是理想的。

在JDK 14中,如果我们运行相同的代码,我们将看到类似以下内容:

Exception in thread "main" java.lang.NullPointerException:
    Cannot read field "c" because "a.b" is null
    at Prog.main(Prog.java:5)

6.2.5 switch扩展标准化

​ 说明:​ switch扩展由预览版改为标准版。

七、JDK15

7.1 说明

​ JDK 15于 2020 年 9 月 15 日全面发布。

JDK15非LTS版本

7.2 新特性

7.2.1 EdDSA

​ 说明:​ EdDSA 是一种现代的椭圆曲线签名方案,与 JDK 中现有的签名方案相比,它具有多个优点。此 JEP 的主要目标是实现 RFC 8032 中标准化的此方案。此新签名方案不会取代 ECDSA。

​ API 使用示例:

// 生成密钥对并签名
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
// Ed25519算法
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();

// 使用KeyFactory构造一个公钥
KeyFactory kf = KeyFactory.getInstance("EdDSA");
boolean xOdd = ...
BigInteger y = ...
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdPoint(xOdd, y));
PublicKey pubKey = kf.generatePublic(pubSpec);

7.2.2 密封类

​ 说明:​ 使用密封类和接口增强 Java 编程语言。密封类和接口限制其他类或接口可以扩展或实现它们。这是JDK 15 中的预览语言功能。

  • 允许类或接口的作者控制哪些代码负责实现它。

  • 提供比访问修饰符更具声明性的方式来限制超类的使用。

  • 通过支持模式的详尽分析来支持模式匹配的未来方向。

使用修饰符sealed,您可以将一个类声明为密封类。密封的类使用reserved关键字permits列出可以直接扩展它的类。子类可以是最终的,非密封的或密封的。

​ 示例:

abstract sealed class Shape permits Circle, Rectangle, Square {
}

final class Circle extends Shape {
}

non-sealed class Rectangle extends Shape {
}

sealed class Square extends Shape {
}

final class TransparentRectangle extends Square {
}

可以看到:

  • 在声明为sealed的类Shape即为密封类,密封类必须有subclass,即子类;

  • 密封类的子类可以为finall、non-sealed、sealed所修饰,如果子类也为sealed,则其也必须有子类。例如Shape的子类Square必须有子类TransparentRectangle;

  • permits修饰符是为了限制可以继承该类的所有子类,例如上述Shape类,就只能被Circle,Rectangle,Square所继承,而其他继承它的类,IDE就会直接报错;如果没有permits修饰符,例如Square类,就可以被任意类所继承。

7.2.3 隐藏类()

​ 说明:​ 引入隐藏类,隐藏类是不能被其他类的字节码直接使用的类。隐藏类旨在供在运行时生成类并通过反射间接使用它们的框架使用。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载。

​ 示例:

创建类Test

package com.dbapp.main;

public class Test {
    public static void println(String str) {
        System.out.println("隐藏类打印:" + str);
    }
}

编译成class,并加载隐藏类:

package com.dbapp.main;


import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {

    public static void main(String[] args) throws Throwable {
        byte[] bytes = Files.readAllBytes(Path.of("/Users/fanyouheng/doc/mysql-to-pg/target/classes/com/dbapp/main/Test.class"));
        Class<?> clazz = MethodHandles.lookup()
                .defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.NESTMATE)
                .lookupClass();
        System.out.println(clazz.getName());
        Method method = clazz.getDeclaredMethod("println", String.class);
        method.invoke(null, "hidden class");
    }

}
// 打印结果
// com.dbapp.main.Test/0x0000000501001c00
// 隐藏类打印:hidden class

7.2.3 ZGC产品化

​ 说明:​ 在这个提案下,Z垃圾收集器(ZGC-Z Garbage Collector)将从一个实验特性升级为产品。 ZGC 集成到2018年9月发布的JDK 11中,是一个可扩展的、低延迟的垃圾收集器。 ZGC 是作为一种实验性的功能引入的,因为 Java 开发人员决定应该小心地、逐步地引入这种规模和复杂性的特性。从那时起,添加了许多改进,从并发类卸载、未使用内存的解除提交、对类数据共享的支持到改进的 NUMA 感知和多线程堆预处理。此外,最大堆大小从4TB增加到16TB。支持的平台包括Linux、Windows和MacOS。

​ -XX:+UseZGCZGC 今天通过命令行选项启用。让 ZGC 成为产品(非实验性)功能意味着-XX:+UnlockExperimentalVMOptions不再需要该选项。

7.2.4 DatagramSocket API

​ 说明:​ java.net.DatagramSocket将和API的底层实现替换java.net.MulticastSocket为更简单、更现代、易于维护和调试的实现。新的实现将很容易适应虚拟线程,目前正在Loom 项目中进行探索。这是JEP 353的后续版本,它已经重新实现了旧版 Socket API。

八、JDK16

8.1 说明

​ JDK 16于 2021 年 3 月 16 日全面发布。Oracle 提供GPL 下的生产就绪二进制文件;其他供应商的二进制文件也将很快推出。此版本的功能和时间表是通过JEP 流程提出和跟踪的,并经JEP 2.0 提案修订 。该版本是使用JDK 发布流程 (JEP 3)生成的。

JDK16非LTS版本

8.2 新特性

8.2.1 启用C++14

​ 说明:​ 通过 JDK 15,JDK 中 C++ 代码使用的语言功能已仅限于 C++98/03 语言标准。在 JDK 11 中,代码已更新以支持使用较新版本的 C++ 标准进行构建,尽管它尚未使用任何新功能。这包括能够使用支持 C++11/14 语言功能的各种编译器的最新版本进行构建。

​ 此 JEP 的目的是正式允许 JDK 内的 C++ 源代码更改以利用 C++14 语言功能,并提供有关哪些功能可以在 HotSpot 代码中使用的具体指导。

8.2.2 矢量API

​ 说明:​ 提供孵化器模块的初始迭代, jdk.incubator.vector以表达向量计算,这些计算在运行时可靠地编译为支持的 CPU 架构上的最佳向量硬件指令,从而实现优于等效标量计算的性能。

8.2.3 ZGC优化

​ 说明:​ 将 ZGC 线程堆栈处理从安全点转移到并发阶段。

  • 从 ZGC 安全点删除线程堆栈处理。

  • 使堆栈处理变得惰性、协作、并发和增量。

  • 从 ZGC 安全点删除所有其他每线程根处理。

  • 提供一种机制,其他 HotSpot 子系统可以通过该机制延迟处理堆栈。

8.2.4 Unix域套接字通道

​ 说明:​ 将 Unix 域 ( AF_UNIX) 套接字支持添加到包中的套接字通道和服务器套接字通道API java.nio.channels。扩展继承的通道机制以支持Unix域套接字通道和服务器套接字通道。

  • 一个新的套接字地址类,java.net.UnixDomainSocketAddress

  • UNIX现有枚举中的常量值java.net.StandardProtocolFamily

  • 指定协议族的新open工厂方法SocketChannel``ServerSocketChannel

  • 更新了 SocketChannelServerSocketChannel规范,以指定 Unix 域套接字的通道的行为方式。

8.2.5 弹性元空间

​ 说明:​ 更及时地将未使用的 HotSpot 类元数据(即元空间)内存返回给操作系统,减少元空间占用空间,并简化元空间代码以降低维护成本。

8.2.6 打包工具

​ 说明:​ 提供jpackage工具,用于打包独立的 Java 应用程序。

​ 该工具是由JEP 343jpackage作为 JDK 14 中的孵化工具引入的。它仍然是 JDK 15 中的孵化工具,以便有时间获得更多反馈。现在它已准备好从孵化升级为生产就绪功能。由于此转换,模块的名称将从 更改为。jpackagejdk.incubator.jpackagejdk.jpackage。

九、JDK17

9.1 说明

​ JDK 17 于 2021 年 9 月 14 日正式发布。Oracle 提供GPL 下的生产就绪二进制文件;其他供应商的二进制文件也将很快推出。 此版本的功能和时间表是通过JEP 流程提出和跟踪的,并经JEP 2.0 提案修订 。该版本是使用JDK 发布流程 (JEP 3)生成的。

Spring 6和SpringBoot 3需要JDK17。

JDK17是LTS版本

9.2 新特性

9.2.1 删除AOT和JIT

​ 说明:​ 删除实验性的基于 Java 的提前 (AOT) 和即时 (JIT) 编译器。该编译器自推出以来几乎没有什么用处,并且维护它所需的工作量很大。保留实验性的Java级JVM编译器接口(JVMCI),以便开发人员可以继续使用外部构建的编译器版本进行JIT编译。

9.2.2 密封类

​ 说明:​ 密封类在JDK17中作为正式功能,和JDK16中相比没有任何改动。

9.2.3 恢复始终严格的浮点语义

​ 说明:​ 使浮点运算始终严格,而不是同时具有严格的浮点语义 ( strictfp) 和略有不同的默认浮点语义。这将恢复语言和 VM 的原始浮点语义,与 Java SE 1.2 中引入严格和默认浮点模式之前的语义相匹配。

9.2.4 统一日志异步刷新

​ 说明:​ 为了避免使用统一日志记录的线程中出现不需要的延迟,用户现在可以请求统一日志记录系统以异步模式运行。这是通过传递命令行选项来完成的-Xlog:async。在异步日志记录模式下,日志站点将所有日志记录消息排队到缓冲区中。独立线程负责将它们刷新到相应的输出。中间缓冲区是有界的。当缓冲区耗尽时,排队的消息将被丢弃。用户可以使用命令行选项来控制中间缓冲区的大小:

-XX:AsyncLogBufferSize=<bytes>

0

评论区