Java 17 是 JDK 最新版别,引进了多项新功用、增强和优化,包含增强的 Switch 表达式、Sealed 类和接口、局部变量类型揣度的扩展、增强的废物收回机制、Vector API 等等。本文将会和Java 8 的代码进行比照,讨论其优下风和实践运用。除此之外,咱们还将概述 Java 17 的其他新特性,以协助您更好地了解和运用 Java 17

Java 17 概述

Java 17 的首要方针是改善 Java 的生产力和开发功率,并增强 Java 的安全性和可维护性。它引进了多项新功用、增强和优化,包含:

  • 局部变量类型揣度的扩展
  • 增强的 Switch 表达式
  • Sealed 类和接口
  • 动态类文件常量
  • 增强的废物收回机制
  • 改善的 ZGC(Z Garbage Collector)
  • Vector API
  • JVM 一致日志体系
  • Java 17的下风

Java 17 还包含一些低级其他增强和优化,如 OpenSSL 的默许加密套件、晋级至 Unicode 13、类嵌套约束的放宽等等。

Java 17 新特性详解

Java 17 引进了许多新特性,以下是一些新特性的具体介绍:

局部变量类型揣度

局部变量类型揣度是在 Java 10 中引进的一个功用,它答应您在声明变量时运用 var 关键字,而不必显式地指定变量的类型。Java 17 扩展了这一功用,答应在方法引证的上下文中运用 var 关键字。

这意味着您能够运用 var 关键字来揣度方法的参数和返回类型。

例如,在 Java 8 中,您可能需求编写以下代码来核算一个字符串列表中一切字符串的总长度:

List<String> list = Arrays.asList("Java", "is", "fun");
int totalLength = 0;
for (String s : list) {
    totalLength += s.length();
}
System.out.println("Total length: " + totalLength);

但在 Java 17 中,您能够运用局部变量类型揣度的扩展来编写更简练的代码:

var list = Arrays.asList("Java", "is", "fun");
var totalLength = list.stream().mapToInt(String::length).sum();
System.out.println("Total length: " + totalLength);

如您所见,这些代码比较于 Java 8 更简练易读。然而,需求留意的是,在某些状况下,运用 var 关键字可能会下降代码的可读性,因而应该慎重运用。

增强的 Switch 表达式

在 Java 12 中引进了 Switch 表达式,它答应您运用更简练的语法编写 Switch 句子。Java 17 增强了 Switch 表达式,包含新的 case 标签、可匹配的形式和箭头运算符等。

新的 case 标签答应您将多个 case 标签放在同一行中,并运用逗号分隔。例如:

int num = 2;
switch (num) {
    case 1, 2, 3 -> System.out.println("Number is 1, 2 or 3");
    case 4 -> System.out.println("Number is 4");
    default -> System.out.println("Number is neither 1, 2, 3 nor 4");
}

可匹配的形式答应您在 case 标签中运用形式匹配。例如:

Object obj = "Hello, World!";
switch (obj) {
    case String s && s.length() > 10 -> System.out.println("String is long");
    case String s -> System.out.println("String is " + s);
    case Integer i -> System.out.println("Integer is " + i);
    default -> System.out.println("Object is of an unexpected type");
}

箭头运算符答应您运用表达式或句子作为 Switch 分支的返回值。例如:

int num = 2;
String result = switch (num) {
    case 1 -> "Number is 1";
    case 2 -> "Number is 2";
    case 3 -> "Number is 3";
    default -> {
        System.out.println("Number is not 1, 2 or 3");
        yield "Number is not 1, 2 or 3";
    }
};
System.out.println(result);

在这个示例中,咱们运用 Switch 表达式来返回一个字符串,依据 num 的值不同返回不同的字符串。假如 num 的值不是 1、2 或 3,则打印一条消息并返回一个默许字符串。

Switch 表达式的这些增强功用能够使代码更简练、易读,一起也进步了代码的可维护性和灵活性。

Sealed 类和接口

Java 17 引进了 Sealed 类和接口,这是一个重要的功用,能够使类和接口愈加安全和可扩展。Sealed 类和接口能够约束它们的子类的数量和类型,然后进步了代码的安全性和可扩展性。

在 Java 8 中,您能够创建一个接口并声明它的方法,然后让其他类完成这个接口。这意味着您不能操控完成接口的类的数量和类型。在 Java 17 中,您能够运用 Sealed 接口来约束完成接口的类的数量和类型。

下面是一个示例:

public sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}

在这个示例中,咱们声明了一个 Sealed 接口 Shape,并运用 permits 关键字指定了 Shape 接口的子类。这意味着只要 Circle、Rectangle 和 Triangle 类能够完成 Shape 接口。假如尝试完成 Shape 接口的其他类将引发编译时过错。

Sealed 类和接口能够协助您操控类的层次结构,进步代码的可维护性和可扩展性。它们还能够协助您编写更安全的代码,因为只要受信赖的类能够完成 Sealed 类和接口。

在 Java 8 中,咱们能够声明一个接口,并让多个类完成它,如下所示:

public interface Shape {
    double area();
}
public class Rectangle implements Shape {
    private double length;
    private double width;
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    @Override
    public double area() {
        return length * width;
    }
}
public class Circle implements Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

在这个示例中,咱们界说了一个 Shape 接口,并让 Rectangle 和 Circle 类完成它。这种规划方法能够让咱们在代码中轻松地引证不同形状的目标,例如:

List<Shape> shapes = Arrays.asList(new Rectangle(10, 20), new Circle(5));
double totalArea = shapes.stream().mapToDouble(Shape::area).sum();
System.out.println("Total area: " + totalArea);

虽然在 Java 8 中,咱们能够经过接口完成多态,可是这种规划方法也存在一些问题。例如,咱们不能操控哪些类能够完成该接口,因而可能会导致意外的结果。此外,假如咱们想要添加新的完成类,咱们需求修正该接口的界说,偏重新编译一切完成类。

在 Java 17 中,咱们能够运用 Sealed 类和接口来处理这些问题。下面是一个示例:

public sealed interface Shape permits Rectangle, Circle {
    double area();
}
public final class Rectangle implements Shape {
    private double length;
    private double width;
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    @Override
    public double area() {
        return length * width;
    }
}
public final class Circle implements Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

在这个示例中,咱们运用了 sealed 关键字来润饰 Shape 接口,并运用 permits 关键字列出了 Rectangle 和 Circle 类作为该接口的完成类。这样,咱们能够操控哪些类能够完成 Shape 接口,并能够确保不会有任何未知的完成类。

此外,咱们还能够运用 Sealed 类和接口来约束类或接口的扩展或完成。这种方法能够进步代码的可读性和可维护性,并使代码愈加安全。

例如,咱们能够将 Shape 接口界说为 non-sealed,这样只要在特定模块内才干运用该接口。假设咱们有一个 Shape 模块,咱们能够界说以下接口:

public non-sealed interface Shape {
    double area();
}

在该模块中,咱们能够界说恣意数量的完成类,例如:

public final class Rectangle implements Shape {
    private double length;
    private double width;
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    @Override
    public double area() {
        return length * width;
    }
}

假如咱们想要在另一个模块中界说新的完成类,咱们需求在 Shape 模块中显式地将该类列为 permitted subclass,例如:

public sealed interface Shape permits Circle, Square {
    double area();
}
public final class Circle implements Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

在这个示例中,咱们将 Circle 类添加到 permitted subclass 列表中,并运用 Square 类来演示其他模块中的非答应完成。

假设咱们在另一个模块中界说以下 Square 类:

public final class Square implements Shape {
    private double side;
    public Square(double side) {
        this.side = side;
    }
    @Override
    public double area() {
        return side * side;
    }
}

因为 Square 类不在 permitted subclass 列表中,因而编译器会发生过错。这样,咱们能够确保只要在 Shape 模块中列出的类才干完成 Shape 接口,然后增强了代码的安全性和可维护性。

总归,Sealed 类和接口是 Java 17 中一个十分实用的特性,它能够进步代码的可读性、可维护性和安全性,而且与 Java 8 比较,运用 sealed 类和接口能够更好地操控类或接口的扩展和完成。

动态类文件常量

动态类文件常量是 Java 17 中的一项新特性,它答应在运转时动态生成常量值。在此之前,Java 中的常量值只能在编译时确认,无法在运转时进行修正。动态类文件常量是经过 Constant Dynamic 类型来完成的,这个类型代表了一个在运转时核算的常量值。

让咱们来看一下一个比如,首先是 Java 8 中的代码:

public class DynamicConstantExample {
    public static final String CONSTANT = "CONSTANT_VALUE";
    public static void main(String[] args) {
        System.out.println(CONSTANT);
    }
}

在 Java 8 中,常量值有必要在编译时确认,因而咱们只能运用静态 final 字段来界说常量。这个比如中的 CONSTANT 字段是一个 final 字符串,它的值在编译时就已经确认了。当咱们运转这个程序时,它会输出 “CONSTANT_VALUE”。

现在,让咱们来看一下运用动态类文件常量的 Java 17 代码:

import java.lang.invoke.*;
public class DynamicConstantExample {
    public static void main(String[] args) throws Throwable {
        CallSite constant = 
            new ConstantCallSite(MethodHandles.constant(String.class, "CONSTANT_VALUE"));
        String value = (String) constant.getTarget().invokeExact();
        System.out.println(value);
    }
}

增强的废物收回机制

Java 17 引进了一些新的废物收回机制的增强,旨在进步功能和削减中止时刻。在此之前,让咱们先了解一下 Java 的废物收回机制。

Java 中的废物收回是经过检测不再被引证的目标来进行的。当一个目标不再被引证时,它就成为了废物。废物收回器会定时运转,并查看哪些目标是废物,然后将其开释。这种主动内存办理机制使 Java 程序员能够防止手动办理内存的复杂性,然后进步开发功率和代码可靠性

在 Java 17 中,废物收回机制有了一些增强,首要包含以下几个方面:

  1. 根据 Epsilon 的废物收回器:Epsilon 是一种实验性的废物收回器,旨在供给一种不履行任何实践废物收回的选项,然后最大化运用程序的吞吐量和推迟。这个选项适用于那些需求削减废物收回的体系,如 Spark 和 Hadoop。
  2. 根据 ZGC 的可选 Epsilon 支撑:在 Java 17 中,Epsilon 废物收回器能够与 ZGC 废物收回器结合运用,然后在某些场景下进步功能。例如,在具有很多内存和高吞吐量要求的运用程序中,运用 ZGC+Epsilon 能够进步 GC 的吞吐量和推迟。
  3. 根据 G1 的可选 JDK 日志符号:在 Java 17 中,能够运用根据 G1 的可选 JDK 日志符号来跟踪废物收回过程中目标的移动状况,然后协助开发人员优化运用程序的功能。
  4. 根据 C++ 的并发废物收回器:在 Java 17 中,引进了一个新的废物收回器,它是根据 C++ 的并发废物收回器。这个新的收回器运用多线程技能来进步废物收回的功率和功能,并削减运用程序的中止时刻。

总的来说,Java 17 引进了一些新的废物收回机制的增强,能够进步废物收回的功率和功能,并削减运用程序的中止时刻。这些增强对于大规模的 Java 运用程序和需求高功能和低推迟的运用程序特别有用。与 Java 8 比较,这些增强使得 Java 废物收回机制愈加灵活和可定

改善的 ZGC(Z Garbage Collector)

ZGC(Z Garbage Collector)是 JDK 11 中引进的一种低推迟废物收回器。Java 17 进一步改善了 ZGC,增加了一些新的特性和优化,使得它在处理大型内存和高并发负载方面表现愈加优异。

ZGC 的首要长处是,它能够处理十分大的堆内存,乃至超过了 16TB。一起,它的废物收回暂停时刻十分短,能够操控在几毫秒内。这对于对呼应时刻有严格要求的运用程序十分有协助。

在 Java 17 中,ZGC 支撑在 Windows 和 macOS 体系上运用,之前只能在 Linux 上运用。此外,Java 17 还增加了一些新的配置选项,能够进一步优化废物收回器的行为。

总归,ZGC 是一个十分强大的废物收回器,能够协助开发人员处理大型内存和高并发负载,一起还能保证低推迟和高吞吐量。假如你的运用程序需求处理很多的数据,而且需求快速响运用户请求,那么 ZGC 可能是一个十分好的选择。

Vector API

Vector API 是 Java 17 中的一个新特性,它答应开发人员以向量化的方法对数组进行操作,然后进步代码的功能和功率。它能够在现代的 CPU 上运用 SIMD(Single Instruction Multiple Data)指令集来并行处理数据,大大进步了数据处理的速度。

在 Java 17 中,Vector API 供给了一组根本的操作函数,包含加、减、乘、除等根本运算,以及一些高级函数,比如求平方根、对数等。这些函数能够对单个数组元素进行操作,也能够对整个数组进行操作。

Vector API 的一个重要特点是,它是可移植的。它能够在不同的 CPU 上运转,而且会主动适应不同的 SIMD 指令集,然后最大程度地进步代码的功能。

Vector API 的运用需求一定的编程技巧和经历。需求运用 Vector API 的开发人员需求了解向量数据类型和向量寄存器的概念,而且需求留意一些约束和约束,比如向量长度有必要是 2、4、8、16 或 32 的倍数等。

总归,Vector API 是 Java 17 中一个十分有用的新特性,它能够协助开发人员愈加高效地处理很多的数据,而且进步代码的功能和功率。可是,运用 Vector API 需求一定的编程技巧和经历,需求开发人员仔细研讨和了解相关的常识。因为笔者也仅仅做了粗略的了解,这儿就不赘述了。

JVM 一致日志体系

Java 17 中引进了一个 JVM 一致日志体系,该体系为开发人员供给了一种标准化的方法来记载 JVM 日志。在曾经的版别中,JVM 日志是由不同的组件各自记载的,运用不同的格局和参数,因而很难进行一致办理和剖析。而现在,一致日志体系能够将一切的 JVM 日志信息收集到一个当地,而且以一致的格局和参数进行记载,便利开发人员进行办理和剖析。

JVM 一致日志体系的完成根据 OpenJDK 中的 JEP 158(Unified Logging),它将一切的日志信息收集到一个单独的日志文件中,该文件能够包含操控台输出、GC 日志、代码缓存信息、线程信息等各种类型的日志信息。开发人员能够经过简略的配置来操控日志的等级、格局和输出方法。

JVM 一致日志体系的优点是清楚明了的。首先,它能够协助开发人员更好地了解 JVM 的内部运转机制,然后更好地进行代码调试和优化。其次,它能够进步生产环境中的问题定位和排查功率,便利快速处理问题。此外,因为日志信息都被一致记载,因而能够更便利地进行日志剖析和监控,然后协助开发人员更好地了解运用程序的运转状况和功能瓶颈。

总归,JVM 一致日志体系是 Java 17 中一个十分有用的新特性,它能够协助开发人员更好地办理和剖析 JVM 日志信息,然后进步代码调试和优化功率,进步生产环境中的问题定位和排查功率,以及更好地了解运用程序的运转状况和功能瓶颈。

Java 17的下风

  1. 晋级本钱:虽然Java 17带来了许多新特性和改善,但对于那些已经运用其他版别Java的企业来说,晋级到Java 17可能需求一些本钱,包含人力本钱和代码更新本钱等。
  2. 兼容性问题:虽然Java 17坚持了向后兼容性,但一些旧版Java运用程序可能需求进行修正才干与Java 17兼容。
  3. 需求更高的硬件要求:Java 17的一些新特性需求更高的硬件要求,包含更多的内存和处理器资源等。因而,对于一些低端硬件的设备来说,可能需求进行晋级才干支撑Java 17。