Java 21新特性

在最新的 Java 21 版本中, Oracle 开发团队为其带来了 15 大功能更新,详细如下:

  • 字符串模板(预览阶段)

    该功能通过将文字文本与嵌入式表达式和处理器相结合来产生专门的结果,从而补充了 Java 现有的字符串文字和文本块。该语言功能和 API 的目的是通过轻松表达包含运行时计算值的字符串来简化 Java 程序的编写。它有望增强表达式的可读性,提高程序的安全性,保持灵活性,并简化接受用非 Java 语言编写的字符串的 API 的使用。

  • 序列集合

    有序集合提案引入了一些接口,用于表示具有已定义遇到顺序的集合。每个集合都有明确定义的第一个和第二个元素,以此类推,直到最后一个元素。提供了一致的 API,用于接受第一个和最后一个元素以及以相反顺序处理元素。该提案的提出的原因是,Java 的集合框架缺乏一种表示具有定义的遇到顺序的元素序列的集合类型。它还缺乏适用于这些集合的一致的操作集。该提案要求定义顺序集合、集合和映射的接口,并将这些接口适应到现有的集合类型层次结构中。所有这些新方法都具有默认实现。

  • 加入 Generational ZGC

    分代 ZGC 的目的是通过扩展 ZGC,维护新旧对象的不同代,从而提高应用程序的性能。年轻的对象往往很早就会死亡;保持独立的世代将允许 ZGC 更频繁地收集年轻对象。使用分代 ZGC 运行的应用程序应能获得以下优势:降低分配停滞的风险、降低堆内存开销和降低垃圾回收 CPU 开销。与非分代 ZGC 相比,这些优势应该可以实现,而不会显著降低吞吐量。

  • 记录模式

    该功能在 JDK 19 和 JDK 20 中都是预览版,主要用于解构记录值。记录模式和类型模式可以嵌套,以实现强大、声明性和可组合的数据导航和处理形式。该提案的目标包括将模式匹配扩展到重组记录类实例,并添加嵌套模式,从而实现更多可组合的数据查询。当前 JEP(JDK 增强提案)中的记录模式提案将最终确定该功能,并根据不断积累的经验和反馈意见进一步完善。

  • switch 模式匹配

    该功能允许 switch 表达式或语句可以根据多个模式(每个模式都有特定的操作)进行测试,从而可以安全、简洁地表达面向数据的复杂查询。该功能最初在 JDK 17 中提出,随后在 JDK 18、JDK 19 和 JDK 20 中得到改进。它将在 JDK 21 中最终完成,并根据反馈和经验进一步完善。与以前的 JEP 相比,主要的变化是删除了括号模式,并允许使用限定的枚举常量(如带有 switch 表达式和语句的 case 常量)。

  • 外部函数与内存 API(第三次预览)

    允许 Java 程序与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数和安全访问外部内存,该 API 使 Java 程序能够调用本地库并处理本机数据,而不会出现 JNI(Java Native Interface)的脆弱性和危险性。该 API 先前在 JDK 20 和 JDK 19 中进行了预览。JDK 21 预览中的改进包括增强的布局路径,增加了一个用于取消引用地址布局的新元素,以及集中管理 Arena 接口中本地段的生命周期;实现了一个后备本地链接器;删除了 VaList。

  • 未命名模式和变量(预览版)

    未命名模式匹配记录组件,但不说明组件名称或类型,而未命名变量可以初始化但不能使用。两者都用下划线字符 _ 表示。该提案旨在通过省略不必要的嵌套模式来提高记录模式的可读性,并通过识别必须声明但不会使用的变量来提高所有代码的可维护性。

  • 虚拟线程

    虚拟线程是一种轻量级线程,有望大幅减少编写、维护和观察高吞吐量并发应用程序的工作量。在 JDK 21 中,虚拟线程将始终支持线程本地变量,并使创建不具备这些变量的虚拟线程成为不可能。对线程本地变量的有保证的支持确保更多的现有库可以不改变地与虚拟线程一起使用,并帮助迁移任务导向的代码以使用虚拟线程。

  • 未命名类和实例主要方法(处于预览阶段)

    该功能的作用是为了让学生能够更容易地编写出第一个 Java 程序,而无需了解为大型程序设计的语言功能。学生无需使用单独的 Java 方言,就能编写单类程序的精简声明,然后随着技能的提高,无缝扩展程序,使用更高级的功能。该提案不仅为学生提供了通往 Java 的平坦道路,还减少了编写脚本和命令行实用程序等简单 Java 程序的繁琐过程。

  • 作用域值(处于预览阶段)

    作用域值(Scoped values)是指允许在线程内和线程间共享不可变数据。作用域值允许在大型程序的组件之间安全地共享数据,而无需使用方法参数。这一提议在 JDK 20 中得到了验证。该计划的目标包括易用性、可理解性、健壮性和性能。

  • 矢量 API(第六个孵化器)

    该 API 表达的矢量计算可在支持的 CPU 架构上可靠地编译为最佳矢量指令,从而实现优于同等标量计算的性能。此前,矢量 API 已在 JDK 16 至 JDK 20 中孵化。最新版本包括性能增强和错误修复。该提案的目标包括:简洁明了、与平台无关、在 x64 和 AArch64 体系结构上提供可靠的运行时编译和性能。

  • 弃用 Windows 32 位 x86 端口

    这个功能更新的目的是在未来的版本中删除该端口。该提案旨在更新构建系统,以便在尝试为 32 位 x86 Windows 配置构建时,发出错误消息。该提案指出,支持 32 位操作的最后一个 Windows 操作系统版本之 Windows 10 将于 2025 年 10 月终止生命周期。

  • 禁止代理的动态加载

    当代理被动态加载到运行中的 JVM 时发出警告。发出这些警告的目的是为将来发布默认禁止加载代理的版本做准备,以改善默认情况下的完整性。该提案的其他目标包括重新评估服务性(涉及对运行中代码的临时更改)和完整性(假定运行中的代码不会被随意更改)之间的平衡,并确保大多数不需要动态加载代理的工具不受影响。从 JDK 21 开始,计划要求应用程序所有者批准动态加载代理,就像启动时加载代理一样。这个改变将使 Java 平台更接近默认情况下的完整性。

  • 密钥封装机制的 API

    这一种通过公开密码学保护对称密钥的加密技术。该提案的一个目标是使应用程序能够使用 KEM 算法,如 RSA 密钥封装机制(RSA-KEM)、椭圆曲线集成加密方案(ECIES)和美国国家标准与技术研究院(NIST)后量子密码标准化过程的候选算法。另一个目标是在更高级别的协议(如传输层安全性(TLS))和密码方案(如混合公钥加密(HPKE))中使用 KEM。安全提供商可以在 Java 代码或本地代码中实现 KEM 算法,并包括在 RFC 9180 中定义的 Diffie-Hellman KEM(DHKEM)的实现。

  • 结构化并发(目前处于预览阶段)

    通过结构化并发 API 简化并发编程,将在不同线程中运行的相关任务组视为单个工作单元。这简化了错误处理和取消操作,提高了可靠性并增强了可观察性。结构化并发之前分别于 2022 年 3 月和 9 月在 JDK 20 和 JDK 19 中孵化,它作为 java.util.concurrent 包中的一个预览 API。这次唯一的重大变化是,StructuredTaskScope::Fork(…) 方法返回的是 [Subtask] 而不是 Future。结构化并发的目标包括促进一种并发编程风格,这种风格可以消除因取消和关闭而产生的常见风险(如线程泄漏和取消延迟),同时提高并发代码的可观察性。

Java 20中有哪些新功能

Java 20将于2023年3月发布,预计将引入一系列变化和新功能。我们准备了一个先睹为快,看看哪些JEP最有可能被JDK 20接受,哪些JEP我们很有希望下一个被接受!
Java 20是计划作为一个非LTS版本,而随后的版本21将被设置为一个具有长期支持(LTS)的版本。

Java开发工具包(JDK)的开发基于JDK增强建议(JEP)的概念。实际上,这些增强建议是JDK发布项目和所有相关开发活动的路线图。
在编写本报告时,JEP索引列出了437项改进建议,以及一些JEP草案和已提交的JEP。

JDK 20中需要哪些JEP?
1、记录模式
作为JDK 19中的第一个预览引入,并预期作为JDK 20中的第二个预览。为了支持数据导航和处理,记录模式简化了与记录组件的使用。在不更改类型模式的语法或语义的情况下,这将扩展更复杂数据查询的模式匹配。

2、switch语句的模式匹配
作为JDK 17中的第一个预览版引入,并预期作为JDK 20中的第四个预览版。

switch (s) {
  case null ->
    { break; }
  case Triangle t
  when t.calculateArea() > 100 ->
    System.out.println("Large triangle");
  default ->
    System.out.println("A shape, possibly a small triangle");
}

3、外部函数&内存(FFM)API
FFM API的根源在于广泛的JEP组合,这导致了它在JDK 19中的第一个预览版,并有望在JDK 20中成为第二个预览版。它允许调用Java程序的外部函数,以与外部代码和数据一起操作,例如在Java运行时之外,而没有JNI的缺点和风险。FFM API的主要目标是拥有一个上级的纯Java开发模型,并支持更广泛的外部内存模型。

4、虚拟线程
作为JDK 19中的第一个预览引入,并预期作为JDK 20中的第二个预览。虚拟线程可提高编写、维护和监视并发应用程序时的效率。它们是轻量级的,支持每个请求的线程可伸缩性,并且可以在对现有代码进行最小更改的情况下应用。传统的Java线程精确地映射到一个操作系统线程,而虚拟线程并不绑定到特定的操作系统线程。因此,创建它们的成本很低,并且可以根据需要创建任意多个。

JDK20发布后,哪些JEP令我们兴奋
1、基元类primitive

JEP 401引入了对一种新的特殊类型的值类的支持,以定义基元类型。由于更好的存储器访问以及因为在CPU内更有效地执行基元操作,基元的使用使得能够改进性能。通过此更新,开发人员可以获得基元的性能优势,同时保留类声明的抽象和其他优势。
为了能够将基元的优点与典型的面向对象结合起来,基元类需要遵守以下两条规则:

  • 原始类中的所有字段都被隐式定义为final,即它们只能在构造函数或初始化函数中设置。
  • 此外,基元类不能具有隐式依赖于声明类的字段。
primitive class Point implements Shape {
   ...
}

2、值对象
传统的Java对象提供标识,即它们的内存位置用于区分一个对象和另一个对象。提供标识在运行时代价很高,而且通常不会在实现中使用。通过这次提交,Java的对象模型扩展了值类和值对象。值类是无标识的,即它们可以提供基元类型的性能优势,同时利用面向对象的概念。当在值对象上使用==时,它们的字段值用于确定对象是否相等,而不是它们的内存位置。请注意,值类的所有字段都是隐式final的,需要在初始化器或构造函数中设置!
实值类使用value,例如:

value class Point implements Shape {
  ...
}

3、Universal generics通用泛型
此提交通过还允许原始类作为类型参数,消除了类型参数必须是引用类型的要求。许多现有的泛型实现将开箱即用地使用这个新增功能,即它们可以用原始类作为其类型参数来实例化。但是,需要特别注意,以防代码对 null 进行赋值,因为原始类将因空指针异常而失败。

4、字符串模板
为了简化常规字符串组合,字符串模板(又名字符串文字)包含在运行时解释的嵌入式表达式。通过向 Java 添加一种新的表达式(字符串模板表达式),JEP 430使得使用包含在运行时计算的值的字符串编写代码变得更加简单。这使得需要用户输入值的程序具有更好的可读性、定义格式语法的灵活性以及更高的安全性。

多线程代码的补充
如果您正在使用多线程代码,请确保检查这些添加项,它们提供了使多线程代码更易于阅读或提供性能提升的选项。
1、Scoped values
使用 extent-local 变量,可以更容易地在 Java 中的线程内和子线程之间共享不可变数据。此更新的目标是简化关于数据流的推理以提高可用性;提高健壮性,以便只有合法的被调用者才能检索调用者共享的数据;并通过启用运行时优化和将共享数据视为不可变来提高性能 虽然此更改不需要从线程局部变量迁移,但它们更适合用于大量虚拟线程。

2、结构化并发
通过引入用于结构化并发的 API, JEP 428可以将在不同线程中运行的不同任务视为一个工作单元。这应该大大简化多线程编程,并通过更容易确保代码的可靠性、可维护性和可观察性来鼓励开发人员应用并发编程。

更多令人兴奋的 JEP

  • Sequenced collections:将 SequencedCollection 接口添加到标准库,提供第一个定义集合中元素顺序的集合。
  • Vector API:与标量计算相比,为了提高性能,这个新的 API 支持在运行时编译矢量计算。
  • 异步堆栈跟踪 API:引入了一个用于异步收集堆栈跟踪的新 API。
  • 类文件 API:添加一个新的 API 来替换 ASM(或 cglib,或其他字节码库),它将提供一种读取、写入和转换 Java 类文件的方法。

 

Java 17 新特性

Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本,这次更新共带来 14 个新功能。

OpenJDK Java 17 下载:https://jdk.java.net/archive/

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

1. JEP 306: 恢复始终严格的浮点语义

既然是恢复严格的浮点语义,那么说明在某个时间点之前,是始终严格的浮点语义的。其实在 Java SE 1.2 之前,所有的浮点计算都是严格的,但是以当初的情况来看,过于严格的浮点计算在当初流行的 x86 架构和 x87 浮点协议处理器上运行,需要大量的额外的指令开销,所以在 Java SE 1.2 开始,需要手动使用关键字 strictfp(strict float point) 才能启用严格的浮点计算。

但是在 2021 年的今天,硬件早已发生巨变,当初的问题已经不存在了,所以从 Java 17 开始,恢复了始终严格的浮点语义这一特性。

扩展strictfp 是 Java 中的一个关键字,大多数人可能没有注意过它,它可以用在类、接口或者方法上,被 strictfp 修饰的部分中的 float 和 double 表达式会进行严格浮点计算。

下面是一个示例,其中的 testStrictfp() 被 strictfp 修饰。

package com.wdbyte;

public class Main {
    public static void main(String[] args) {
        testStrictfp();
    }

    public strictfp static void testStrictfp() {
        float aFloat = 0.6666666666666666666f;
        double aDouble = 0.88888888888888888d;
        double sum = aFloat + aDouble;
        System.out.println("sum: " + sum);
    }
}

2. JEP 356:增强的伪随机数生成器

为伪随机数生成器 RPNG(pseudorandom number generator)增加了新的接口类型和实现,让在代码中使用各种 PRNG 算法变得容易许多。

这次增加了 RandomGenerator 接口,为所有的 PRNG 算法提供统一的 API,并且可以获取不同类型的 PRNG 对象流。同时也提供了一个新类 RandomGeneratorFactory 用于构造各种 RandomGenerator 实例,在 RandomGeneratorFactory 中使用 ServiceLoader.provider 来加载各种 PRNG 实现。

下面是一个使用示例:随便选择一个 PRNG 算法生成 5 个 10 以内的随机数。

package com.wdbyte.java17;

import java.util.Date;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.Stream;

/**
 * @author niulang
 */
public class JEP356 {

    public static void main(String[] args) {
        RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
        // 使用时间戳作为随机数种子
        RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            System.out.println(randomGenerator.nextInt(10));
        }
    }
}

得到输出:

7
3
4
4
6

你也可以遍历出所有的 PRNG 算法。

RandomGeneratorFactory.all().forEach(factory -> {
    System.out.println(factory.group() + ":" + factory.name());
});

得到输出:

LXM:L32X64MixRandom
LXM:L128X128MixRandom
LXM:L64X128MixRandom
Legacy:SecureRandom
LXM:L128X1024MixRandom
LXM:L64X128StarStarRandom
Xoshiro:Xoshiro256PlusPlus
LXM:L64X256MixRandom
Legacy:Random
Xoroshiro:Xoroshiro128PlusPlus
LXM:L128X256MixRandom
Legacy:SplittableRandom
LXM:L64X1024MixRandom

可以看到 Legacy:Random 也在其中,新的 API 兼容了老的 Random 方式,所以你也可以使用新的 API 调用 Random 类生成随机数。

// 使用 Random
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("Random");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
for (int i = 0; i < 5; i++) {
    System.out.println(randomGenerator.nextInt(10));
}

扩展阅读:增强的伪随机数生成器

3. JEP 382:使用新的 macOS 渲染库

macOS 为了提高图形的渲染性能,在 2018 年 9 月抛弃了之前的 OpenGL 渲染库 ,而使用了 Apple Metal 进行代替。Java 17 这次更新开始支持 Apple Metal,不过对于 API 没有任何改变,这一些都是内部修改。

扩展阅读:macOS Mojave 10.14 Release NotesApple Metal

4. JEP 391:支持 macOS/AArch64 架构

起因是 Apple 在 2020 年 6 月的 WWDC 演讲中宣布,将开启一项长期的将 Macintosh 计算机系列从 x64 过度到 AArch64 的长期计划,因此需要尽快的让 JDK 支持 macOS/AArch64 。

Linux 上的 AArch64 支持以及在 Java 16 时已经支持,可以查看之前的文章了解。

扩展:Java 16 新功能介绍 – JEP 386

5. JEP 398:删除已弃用的 Applet API

Applet 是使用 Java 编写的可以嵌入到 HTML 中的小应用程序,嵌入方式是通过普通的 HTML 标记语法,由于早已过时,几乎没有场景在使用了。

示例:嵌入 Hello.class

<applet code="Hello.class" height=200 width=200></applet>

Applet API 在 Java 9 时已经标记了废弃,现在 Java 17 中将彻底删除。

6. JEP 403:更强的 JDK 内部封装

如 Java 16 的 JEP 396 中描述的一样,为了提高 JDK 的安全性,使 --illegal-access 选项的默认模式从允许更改为拒绝。通过此更改,JDK 的内部包和 API(关键内部 API 除外)将不再默认打开。

但是在 Java 17 中,除了 sun.misc.Unsafe ,使用 --illegal-access 命令也不能打开 JDK 内部的强封装模式了,除了 sun.misc.Unsafe API .

在 Java 17 中使用 --illegal-access 选项将会得到一个命令已经移除的警告。

➜  bin ./java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
➜  bin ./java --illegal-access=warn
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0

扩展阅读:JEP 403:更强的 JDK 内部封装Java 16 新功能介绍

7. JEP 406:switch 的类型匹配(预览)

如 instanceof 一样,为 switch 也增加了类型匹配自动转换功能。

在之前,使用 instanceof 需要如下操作:

if (obj instanceof String) {
    String s = (String) obj;    // grr...
    ...
}

多余的类型强制转换,而现在:

if (obj instanceof String s) {
    // Let pattern matching do the work!
    ...
}

switch 也可以使用类似的方式了。

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

对于 null 值的判断也有了新的方式。

// Java 17 之前
static void testFooBar(String s) {
    if (s == null) {
        System.out.println("oops!");
        return;
    }
    switch (s) {
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}
// Java 17
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");
    }
}

扩展阅读: JEP 406:switch 的类型匹配(预览)

8. JEP 407:移除 RMI Activation

移除了在 JEP 385 中被标记废除的 RMI(Remote Method Invocation)Activation,但是 RMI 其他部分不会受影响。

RMI Activation 在 Java 15 中的 JEP 385 已经被标记为过时废弃,至今没有收到不良反馈,因此决定在 Java 17 中正式移除。

扩展阅读: JEP 407:移除 RMI Activation

9. JEP 409:密封类(Sealed Classes)

Sealed Classes 在 Java 15 中的 JEP 360 中提出,在 Java 16 中的 JEP 397 再次预览,现在 Java 17 中成为正式的功能,相比 Java 16 并没有功能变化,这里不再重复介绍,想了解的可以参考之前文章。

扩展阅读:Java 16 新功能介绍JEP 409: Sealed Classes

10. JEP 401:移除实验性的 AOT 和 JIT 编译器

在 Java 9 的 JEP 295 中,引入了实验性的提前编译 jaotc 工具,但是这个特性自从引入依赖用处都不太大,而且需要大量的维护工作,所以在 Java 17 中决定删除这个特性。

主要移除了三个 JDK 模块:

  1. jdk.aot – jaotc 工具。
  2. Jdk.internal.vm.compiler – Graal 编译器。
  3. jdk.internal.vm.compiler.management

同时也移除了部分与 AOT 编译相关的 HotSpot 代码:

  1. src/hotspot/share/aot — dumps and loads AOT code
  2. Additional code guarded by #if INCLUDE_AOT

11. JEP 411:弃用 Security Manager

Security Manager 在 JDK 1.0 时就已经引入,但是它一直都不是保护服务端以及客户端 Java 代码的主要手段,为了 Java 的继续发展,决定弃用 Security Manager,在不久的未来进行删除。

@Deprecated(since="17", forRemoval=true)
public class SecurityManager {
	// ...
}

12. JEP 412:外部函数和内存 API (孵化)

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

这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign来编译和运行 Java 代码。

历史

  • Java 14 JEP 370引入了外部内存访问 API(孵化器)。
  • Java 15 JEP 383引入了外部内存访问 API(第二孵化器)。
  • Java 16 JEP 389引入了外部链接器 API(孵化器)。
  • Java 16 JEP 393引入了外部内存访问 API(第三孵化器)。
  • Java 17 JEP 412引入了外部函数和内存 API(孵化器)。

扩展阅读:JEP 412:外部函数和内存 API (孵化)

13. JEP 414:Vector API(二次孵化)

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

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

14. JEP 415:指定上下文的反序列化过滤器

Java 中的序列化一直都是非常重要的功能,如果没有序列化功能,Java 可能都不会占据开发语言的主导地位,序列化让远程处理变得容易和透明,同时也促进了 Java EE 的成功。

但是 Java 序列化的问题也很多,它几乎会犯下所有的可以想象的错误,为开发者带来持续的维护工作。但是要说明的是序列化的概念是没有错的,把对象转换为可以在 JVM 之间自由传输,并且可以在另一端重新构建的能力是完全合理的想法,问题在于 Java 中的序列化设计存在风险,以至于爆出过很多和序列化相关的漏洞。

反序列化危险的一个原因是,有时候我们不好验证将要进行反序列化的内容是否存在风险,而传入的数据流可以自由引用对象,很有可能这个数据流就是攻击者精心构造的恶意代码。

所以,JEP 415 允许在反序列化时,通过一个过滤配置,来告知本次反序列化允许或者禁止操作的类,反序列化时碰到被禁止的类,则会反序列化失败。

14.1. 反序列化示例

假设 Dog 类中的 Poc 是恶意构造的类,但是正常反序列化是可以成功的。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
            objectOutputStream.writeObject(dog);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    }
}

class Dog implements Serializable {
    private String name;
    private Poc poc;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" + "name='" + name + '\'' + '}';
    }
		// get...set...
}

class Poc implements Serializable{

}

输出结果:

Dog{name='哈士奇'}

14.2. 反序列化过滤器

在 Java 17 中可以自定义反序列化过滤器,拦截不允许的类。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
            objectOutputStream.writeObject(dog);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        // 允许 com.wdbyte.java17.Dog 类,允许 java.base 中的所有类,拒绝其他任何类
        ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
                        "com.wdbyte.java17.Dog;java.base/*;!*");
        objectInputStream.setObjectInputFilter(filter);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    }
}

class Dog implements Serializable {
    private String name;
    private Poc poc;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" + "name='" + name + '\'' + '}';
    }
		// get...set...
}

class Poc implements Serializable{
}

这时反序列化会得到异常。

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
	at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2053)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1907)
	....

扩展阅读:JEP 415:指定上下文的反序列化过滤器

 

文章转自:https://www.cnblogs.com/niumoo/p/15522730.html

Java JDK10新特性

Java10它号称有109项新特性,包含12个JEP。

需要注意的是,本次Java10并不是Oracle的官方LTS版本,所以咱们可以先了解新特性。然后坐等java11的发布再考虑在生产中使用吧

特性列表:

  • 局部变量的类型推断 var关键字
  • GC改进和内存管理 并行全垃圾回收器 G1
  • 垃圾回收器接口
  • 线程-局部变量管控
  • 合并 JDK 多个代码仓库到一个单独的储存库中
  • 新增API:ByteArrayOutputStream
  • 新增API:List、Map、Set
  • 新增API:java.util.Properties
  • 新增API: Collectors收集器
  • 其它特性

1、局部变量的类型推断 var关键字

这个新功能将为Java增加一些语法糖 – 简化它并改善开发者体验。新的语法将减少与编写Java相关的冗长度,同时保持对静态类型安全性的承诺。

这可能是Java10给开发者带来的最大的一个新特性。下面主要看例子:

public static void main(String[] args) {
    var list = new ArrayList<String>();
    list.add("hello,world!");
    System.out.println(list);

}

这是最平常的使用。注意赋值语句右边,最好写上泛型类型,否则会有如下情况:

public static void main(String[] args) {
    var list = new ArrayList<>();
    list.add("hello,world!");
    list.add(1);
    list.add(1.01);
    System.out.println(list);
}

list什么都可以装,非常的不安全了。和js等语言不同的是,毕竟Java还是强类型的语言,所以下面语句是编译报错的:

public static void main(String[] args) {
    var list = new ArrayList<String>();
    list.add("hello,world!");
    System.out.println(list);

    list = new ArrayList<Integer>(); //编译报错
}

!!!:下面几点使用限制

  • 局部变量初始化
  • for循环内部索引变量
  • 传统的for循环声明变量
  • 方法参数
  • 全局变量
  • 构造函数参数
  • 方法返回类型
  • 字段
  • 捕获表达式(或任何其他类型的变量声明)
public static void main(String[] args) {
    //局部变量初始化
    var list = new ArrayList<String>();
    
    //for循环内部索引变量
    for (var s : list) {
        System.out.println(s);
    }
    
    //传统的for循环声明变量
    for (var i = 0; i < list.size(); i++) {
        System.out.println(i);
    }
}

 

public static var list = new ArrayList<String>(); //编译报错
public static List<String> list = new ArrayList<>(); //正常编译通过

2、GC改进和内存管理 并行全垃圾回收器 G1

JDK 10中有2个JEP专门用于改进当前的垃圾收集元素。
Java 10的第二个JEP是针对G1的并行完全GC(JEP 307),其重点在于通过完全GC并行来改善G1最坏情况的等待时间。G1是Java 9中的默认GC,并且此JEP的目标是使G1平行。

3、垃圾回收器接口

这不是让开发者用来控制垃圾回收的接口;而是一个在 JVM 源代码中的允许另外的垃圾回收器快速方便的集成的接口。

4、线程-局部变量管控

这是在 JVM 内部相当低级别的更改,现在将允许在不运行全局虚拟机安全点的情况下实现线程回调。这将使得停止单个线程变得可能和便宜,而不是只能启用或停止所有线程。

5、合并 JDK 多个代码仓库到一个单独的储存库中

在 JDK9 中,有 8 个仓库: root、corba、hotspot、jaxp、jaxws、jdk、langtools 和 nashorn 。在 JDK10 中这些将被合并为一个,使得跨相互依赖的变更集的存储库运行 atomic commit (原子提交)成为可能。

6、新增API:ByteArrayOutputStream

String toString(Charset): 重载 toString(),通过使用指定的字符集解码字节,将缓冲区的内容转换为字符串。

7、新增API:List、Map、Set

这3个接口都增加了一个新的静态方法,copyOf(Collection)。这些函数按照其迭代顺序返回一个不可修改的列表、映射或包含给定集合的元素的集合。

8、新增API:java.util.Properties

增加了一个新的构造函数,它接受一个 int 参数。这将创建一个没有默认值的空属性列表,并且指定初始大小以容纳指定的元素数量,而无需动态调整大小。还有一个新的重载的 replace 方法,接受三个 Object 参数并返回一个布尔值。只有在当前映射到指定值时,才会替换指定键的条目。

9、新增API: Collectors收集器

toUnmodifiableList():
toUnmodifiableSet():
toUnmodifiableMap(Function, Function):
toUnmodifiableMap(Function, Function, BinaryOperator):
这四个新方法都返回 Collectors ,将输入元素聚集到适当的不可修改的集合中。

10、其它特性

线程本地握手(JEP 312)

其他Unicode语言 – 标记扩展(JEP 314)

基于Java的实验性JIT编译器

根证书颁发认证(CA)

删除工具javah(JEP 313)

从JDK中移除了javah工具,这个很简单并且很重要。

Java JDK9的新特性

(一):jdk和jre的改变

JDK和JRE已经在Java SE 9中进行了模块化处理。在Java SE 9之前,JDK构建系统用于生成两种类型的运行时映像 ——Java运行时环境(JRE)和Java开发工具包(JDK)。 JRE是Java SE平台的完整实现,JDK包含了JRE和开发工具和类库。

可下图显示了Java SE 9之前的JDK安装中的主目录。JDK_HOME是安装JDK的目录。 如果你只安装了JRE,那么你只有在jre目录下的目录。

1556257207-9564-2ud3f7y6go

在 Java SE 9之前,JDK中:

bin目录用于包含命令行开发和调试工具,如javac,jar和javadoc。 它还用于包含Java命令来启动Java应用程序。

include目录包含在编译本地代码时使用的C/C++头文件。

lib目录包含JDK工具的几个JAR和其他类型的文件。 它有一个tools.jar文件,其中包含javac编译器的Java类。

jre\bin目录包含基本命令,如java命令。 在Windows平台上,它包含系统的运行时动态链接库(DLL)。

jre\lib目录包含用户可编辑的配置文件,如.properties和.policy文件。

jre\lib\approved目录包含允许使用标准覆盖机制的JAR。 这允许在Java社区进程之外创建的实施标准或独立技术的类和接口的更高版本被并入Java平台。 这些JAR被添加到JVM的引导类路径中,从而覆盖了Java运行时中存在的这些类和接口的任何定义。

jre\lib\ext目录包含允许扩展机制的JAR。 该机制通过扩展类加载器(该类加载器)加载了该目录中的所有JAR,该引导类加载器是系统类加载器的子进程,它加载所有应用程序类。 通过将JAR放在此目录中,可以扩展Java SE平台。 这些JAR的内容对于在此运行时映像上编译或运行的所有应用程序都可见。

jre\lib目录包含几个JAR。 rt.jar文件包含运行时的Java类和资源文件。 许多工具依赖于rt.jar文件的位置。

jre\lib目录包含用于非Windows平台的动态链接本地库。

jre\lib目录包含几个其他子目录,其中包含运行时文件,如字体和图像。

Java SE 9调整了JDK的目录层次结构,并删除了JDK和JRE之间的区别。 下图显示了Java SE 9中JDK安装的目录。JDK 9中的JRE安装不包含include和jmods目录。

1556257207-3382-ykfybxx5s6

在Java SE 9 的JDK中: 没有名为jre的子目录。 bin目录包含所有命令。 在Windows平台上,它继续包含系统的运行时动态链接库。 conf目录包含用户可编辑的配置文件,例如以前位于jre\lib目录中的.properties和.policy文件。 include目录包含要在以前编译本地代码时使用的C/C++头文件。 它只存在于JDK中。 jmods目录包含JMOD格式的平台模块。 创建自定义运行时映像时需要它。 它只存在于JDK中。 legal 目录包含法律声明。 lib目录包含非Windows平台上的动态链接本地库。 其子目录和文件不应由开发人员直接编辑或使用。

(二):访问资源

资源是应用程序使用的数据,例如图像,音频,视频,文本文件等。Java提供了一种通过在类路径上定位资源来访问资源的位置无关的方式。 需要与在JAR中打包类文件相同的方式打包资源,并将JAR添加到类路径。 通常,类文件和资源打包在同一个JAR中。 访问资源是每个Java开发人员执行的重要任务。

1. 在JDK 9之前访问资源

在Java代码中,资源由资源名称标识,资源名称是由斜线(/)分隔的一串字符串。 对于存储在JAR中的资源,资源名称仅仅是存储在JAR中的文件的路径。

例如,在JDK 9之前,存储在rt.jar中的java.lang包中的Object.class文件是一个资源,其资源名称是java/lang/Object.class。 在JDK 9之前,可以使用以下两个类中的方法来访问资源:

java.lang.Class java.lang.ClassLoader资源由ClassLoader定位。 一个Class代理中的资源寻找方法到它的ClassLoader。 因此,一旦了解ClassLoader使用的资源加载过程,将不会在使用Class类的方法时遇到问题。

在两个类中有两种不同的命名实例方法:

  • URL getResource(String name)
  • InputStream getResourceAsStream(String name)

两种方法都会以相同的方式找到资源。 它们的差异仅在于返回类型。 第一个方法返回一个URL,而第二个方法返回一个InputStream。 第二种方法相当于调用第一种方法,然后在返回的URL对象上调用openStream()。

ClassLoader类包含三个额外的查找资源的静态方法:

  • static URL getSystemResource(String name)
  • static InputStream getSystemResourceAsStream(String name)
  • static Enumeration<URL> getSystemResources(String name)

这些方法使用系统类加载器(也称为应用程序类加载器)来查找资源。 第一种方法返回找到的第一个资源的URL。 第二种方法返回找到的第一个资源的InputStream。 第三种方法返回使用指定的资源名称找到的所有资源的URL枚举。 要找到资源,有两种类型的方法可以从——getSystemResource*和getResource*中进行选择。 在讨论哪种方法是最好的之前,重要的是要了解有两种类型的资源: 系统资源非系统资源你必须了解他们之间的区别,以了解资源查找机制。

系统资源是在bootstrap类路径,扩展目录中的JAR和应用程序类路径中找到的资源。

非系统资源可以存储在除路径之外的位置,例如在特定目录,网络上或数据库中。

getSystemResource()方法使用应用程序类加载程序找到一个资源,委托给它的父类,它是扩展类加载器,后者又委托给它的父类(引导类加载器)。如果你的应用程序是独立的应用程序,并且它只使用三个内置的JDK类加载器,那么你将很好的使用名为getSystemResource *的静态方法。它将在类路径中找到所有资源,包括运行时映像中的资源,如rt.jar文件。如果你的应用程序是在浏览器中运行的小程序,或在应用程序服务器和Web服务器中运行的企业应用程序,则应使用名为getResource*的实例方法,它可以使用特定的类加载器来查找资源。如果在Class对象上调用getResource*方法,则会使用当前类加载器(加载Class对象的类加载器)来查找资源。 传递给ClassLoader类中所有方法的资源名称都是绝对的,它们不以斜线(/)开头。

例如,当调用ClassLoader的getSystemResource()方法时,将使用java/lang/Object.class作为资源名称。 Class类中的资源查找方法可以指定绝对和相对资源名称。绝对资源名称以斜线开头,而相对资源名称不用。 当使用绝对名称时,Class类中的方法会删除前导斜线并委派给加载Class对象的类加载器来查找资源。 以下调用 Test.class.getResource(“/resources/test.config”); 会被转换成 Test.class.getClassLoader().getResource(“resources/test.config”); 当使用相对名称时,Class类中的方法预先添加了包名称,在使用斜线后跟斜线替换包名中的点,然后再委托加载Class对象的类加载器来查找资源。 假设测试类在com.jdojo.test包中,以下调用: Test.class.getResource(“resources/test.config”); 会被转换成 Test.class.getClassLoader().getResource(“com/jdojo/test/resources/test.config”);

2. 在JDK 9 中访问资源

在JDK 9之前,可以从类路径上的任何JAR访问资源。 在JDK 9中,类和资源封装在模块中。 在第一次尝试中,JDK 9设计人员强制执行模块封装规则,模块中的资源必须对该模块是私有的,因此它们只能在该模块内的代码中访问。 虽然这个规则在理论上看起来很好,但是对于跨模块共享资源的框架和加载的类文件作为来自其他模块的资源,就会带来问题。 为了有限地访问模块中的资源,做了一些妥协,但是仍然强制执行模块的封装。

JDK 9包含三类资源查找方法:

  • java.lang.Class java.lang.ClassLoader
  • java.lang.Module Class
  • ClassLoader类没新增任何新的方法。

Module类包含一个getResourceAsStream(String name)方法,如果找到该资源,返回一个InputStream;否则返回null。

(三):模块化

模块化特性是Java 9 最大的一个特性,Java 9起初的代号就叫Jigsaw,后来被更改为Modularity,Modularity提供了类似于OSGI框架的功能,模块之间存在相互的依赖关系,可以导出一个公共的API,并且隐藏实现的细节,Java提供该功能的主要的动机在于,减少内存的开销,我们大家都知道,在JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去,模块化可以根据模块的需要加载程序运行需要的class,那么JVM是如何知道需要加载那些class的呢?这就是在Java 9 中引入的一个新的文件module.java我们大致来看一下一个例子(module-info.java)

module com.lanxinbase.java9.modules.car
{
    requires com.lanxinbase.java9.modules.engines;
    exports com.lanxinbase.java9.modules.car.handling;
}

Java 平台级模块系统

Java 9的定义功能是一套全新的模块系统。当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长。这时候就得面对两个基础的问题: 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到, 这样就会导致无意中使用了并不想被公开访问的 API。此外,类路径本身也存在问题: 你怎么知晓所有需要的 JAR 都已经有了, 或者是不是会有重复的项呢? 模块系统把这俩个问题都给解决了。 模块化的 JAR 文件都包含一个额外的模块描述器。在这个模块描述器中, 对其它模块的依赖是通过 “requires” 来表示的。另外, “exports” 语句控制着哪些包是可以被其它模块访问到的。所有不被导出的包默认都封装在模块的里面。如下是一个模块描述器的示例,存在于 “module-info.java” 文件中:

module blog
{
    exports com.pluralsight.blog;

    requires cms;
}

我们可以如下展示模块:

1556257208-2617-2yi6gh3ifi

请注意,两个模块都包含封装的包,因为它们没有被导出(使用橙色盾牌可视化)。 没有人会偶然地使用来自这些包中的类。Java 平台本身也使用自己的模块系统进行了模块化。通过封装 JDK 的内部类,平台更安全,持续改进也更容易。当启动一个模块化应用时, JVM 会验证是否所有的模块都能使用,这基于 requires 语句——比脆弱的类路径迈进了一大步。模块允许你更好地强制结构化封装你的应用并明确依赖。

(四):JShel,交互式 Java REPL

许多语言已经具有交互式编程环境,Java 现在加入了这个俱乐部。您可以从控制台启动 jshell ,并直接启动输入和执行 Java 代码。 jshell 的即时反馈使它成为探索 API 和尝试语言特性的好工具。

1556257208-9610-rhrwnmw0e4

测试一个 Java 正则表达式是一个很好的说明 jshell 如何使您的生活更轻松的例子。 交互式 shell 还可以提供良好的教学环境以及提高生产力,您可以在此了解更多信息。在教人们如何编写 Java 的过程中,不再需要解释 “public static void main(String [] args)” 这句废话。

JShell详解

JShell项目是第一个官方的Java REPL (Read-Eval-Print-Loop的缩写,即交互式编程环境),是一种命令行工具。它允许你无需使用类或者方法包装来执行Java语句。它与Python的解释器类似,或其它本地支持REPL的JVM语言,如Scala和Groovy。在Java 9新特性中,这绝对是更有趣的特性之一。下面让我们看看JShell一些最有趣的特性。

1 分号对于纯语句是可选的

1556257207-2708-jqrnq48x1g

实际上,在发起的一个关于未来Java特性的调查中,该特性是受多数人认可的。当然分号仍被保留了下来,无论是作为终结符还是分隔符。REPL允许一次性键入纯表达式和语句,因此分号对于JShell终端用例是可选的。

2 没有受检异常

如果你一直担心受检异常会毁掉你的REPL经历——无需再担心,JShell在后台为你隐藏好了。在下面的例子中,本应当强迫我们捕获一个IOException,却没有出现。下面的例子是我们在读取和打印一个文件,不需要处理IOException。

1556257207-9420-tfpltc1q9b

有一种情况的确会有受检异常弹出,就是当我们尝试运行一个线程,并在里面使用了 Thread.sleep() 语句。由于这是一个整体的方法而非单独的纯语句,它必须是完全有效的Java语句:

1556257213-1371-u3wi84h1ak

3 Java表达式

JShell终端还可以自己计算Java表达式。字符串连接、方法回调、算法,诸如此类。基本上,任何你可以包装在 System.out.println(/ expression here /) 里的都可以计算。正如你可能已经知道到的其它计算方式,它会立即将结果赋给自己的一个变量并打印出来。

1556257213-8809-uj36cf8swg

4 向前引用

JShell给向前引用提供了很棒的支持,所以你在定义方法时可以引用其他方法或变量,且这些方法或变量仅会在一段时间后被定义。这是AdoptOpenJDK提供的REPL指南中的一个例子:

1556257213-9160-v91kln4ao1

5 REPL网络

使用JShell时,我们不会受限于机器和网络访问,这带来了一些有趣的机会。例如,想想把它当做一个终端来与服务器交流,远程连接到服务器并且从外面控制一些参数。另一个选择是查询数据库,这里真的是有无限可能。

1556257214-7611-sg4ejltya6

(五):集合工厂方法

通常,您希望在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它。 实例化集合,几个 “add” 调用,使得代码重复。 Java 9,添加了几种集合工厂方法:

Set<Integer> ints = Set.of(1, 2, 3);
List<String> strings = List.of("first", "second");

除了更短和更好阅读之外,这些方法也可以避免您选择特定的集合实现。 事实上,从工厂方法返回已放入数个元素的集合实现是高度优化的。这是可能的,因为它们是不可变的:在创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException” 。

(六):改进的 Stream API

长期以来,Stream API 都是 Java 标准库最好的改进之一。通过这套 API 可以在集合上建立用于转换的申明管道。在 Java 9 中它会变得更好。Stream 接口中添加了 4 个新的方法:dropWhile, takeWhile, ofNullable。还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代:

IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);

第二个参数是一个 Lambda,它会在当前 IntStream 中的元素到达 100 的时候返回 true。因此这个简单的示例是向控制台打印 1 到 99。 除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stram将一个 Optional 对象转换为一个(可能是空的) Stream 对象:

Stream<Integer> s = Optional.of(1).stream();

在组合复杂的 Stream 管道时,将 Optional 转换为 Stream 非常有用。

(七):多版本兼容JAR

多版本兼容JAR这个特性对于库的维护者而言是个特别好的消息。当一个新版本的 Java 出现的时候,你的库用户要花费数年时间才会切换到这个新的版本。这就意味着库得去向后兼容你想要支持的最老的 Java 版本 (许多情况下就是 Java 6 或者 7)。这实际上意味着未来的很长一段时间,你都不能在库中运用 Java 9 所提供的新特性。幸运的是,多版本兼容 JAR 功能能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本:

multirelease.jar
    ├──META-INF
    │   └──versions
    │       └── 9
            │           └──multirelease
    │               └──Helper .class
    ├──multirelease
        ├──Helper .class
        └──Main .class

在上述场景中, multirelease.jar 可以在 Java 9 中使用, 不过 Helper 这个类使用的不是顶层的 multirelease.Helper 这个 class, 而是处在“META-INF/versions/9”下面的这个。这是特别为 Java 9 准备的 class 版本,可以运用 Java 9 所提供的特性和库。同时,在早期的 Java 诸版本中使用这个 JAR 也是能运行的,因为较老版本的 Java 只会看到顶层的这个 Helper 类。

(八):私有接口方法

Java 8 为我们带来了接口的默认方法。 接口现在也可以包含行为,而不仅仅是方法签名。 但是,如果在接口上有几个默认方法,代码几乎相同,会发生什么情况? 通常,您将重构这些方法,调用一个可复用的私有方法。 但默认方法不能是私有的。 将复用代码创建为一个默认方法不是一个解决方案,因为该辅助方法会成为公共API的一部分。 使用 Java 9,您可以向接口添加私有辅助方法来解决此问题:

public interface MyInterface {

    void normalInterfaceMethod();

    default void interfaceMethodWithDefault() {
        init();
    }

    default void anotherDefaultMethod() {
        init();
    }

    // This method is not part of the public API exposed by MyInterface
    private void init() {
        System.out.println("Initializing");
    }
}

如果您使用默认方法开发 API ,那么私有接口方法可能有助于构建其实现。

(九):HTTP/2

Java 9 中有新的方式来处理 HTTP 调用。这个迟到的特性用于代替老旧的 HttpURLConnection API,并提供对 WebSocket 和 HTTP/2 的支持。注意:新的 HttpClient API 在 Java 9 中以所谓的孵化器模块交付。也就是说,这套 API 不能保证 100% 完成。不过你可以在 Java 9 中开始使用这套 API:

HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create("http://www.google.com"))
                .header("User-Agent", "Java")
                .GET()
                .build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());

除了这个简单的请求/响应模型之外,HttpClient 还提供了新的 API 来处理 HTTP/2 的特性,比如流和服务端推送。

 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |