从Java 11升级到Java 17
成富_Alex
编辑于 2021年10月12日 03:30
收录于文集
共4篇

Java 17已经在2021年9月14号发布。作为继Java 8,Java 11之后的第三个LTS版本,Java 17必将成为升级Java的下一个目标。由于Java发布周期的修改,对大部分项目来说,只有LTS版本才是会考虑使用的版本。Java 17的更新维护至少会到2029年。

如果你想了解Java的版本更新周期,可以参考下面这个视频。

如果你当前使用的是Java 11,那么升级到Java 17之后,可以使用很多新的特性,也有其他需要注意的地方。本文对这些新特性做一下总结。


记录类型

记录类型是Java原生的创建值对象(Value Object)的方式。可以替代常用的Lombok中的@Value 注解。可以类比Kotlin中的数据类(data class)和Scala中的case class。

记录类型是一组属性的容器。每一个属性称为记录类型的组件。下面代码中的记录类型 GeoLocation 包含了两个组件:lng 和 lat。

代码块
clike
自动换行
复制代码
public record GeoLocation(double lng, double lat) {

}
复制成功

在创建记录类型时,只需要声明组件的名称和类型,编译器会自动生成与之相关的方法,包括:

  • 对全部组件进行初始化的构造器。

  • 与组件名称同名的获取组件值的方法。

  • 根据组件值进行比较的 equals 和 hashCode 方法。

  • 输出组件值的 toString 方法。

如果需要对组件的值进行验证或规范化处理,可以添加自定义的构造器来添加相关的逻辑。如果仅需要对部分组件添加自定义处理逻辑,可以使用紧凑形式的构造器。

记录类型可以嵌套,方便表示复杂的对象结构。

记录类型可以添加在方法中,用来保存方法执行中所需的中间结果。

关于记录类型的详细介绍,请参考下面这个视频。


密封类

密封类提供了一种方式来限制类的继承。父类可以指定所允许的子类。

密封类的子类可以有三种选择:

  • 声明为 final,禁止继承。

  • 声明为 sealed,继续限制继承。

  • 声明为 non-sealed,恢复会默认的开放继承模式。

这三种选择必须选一个,也只能选一个。

接口也是可以密封的。

下面的代码是密封类的使用示例。

代码块
clike
自动换行
复制代码
public abstract sealed class Shape {

  final class Circle extends Shape {

  }

  sealed class Rectangle extends Shape {

    final class Square extends Rectangle {

    }
  }

  non-sealed class FreeFormShape extends Shape {

  }
}
复制成功

关于密封类的详细介绍,请参考下面的这个视频。


switch 表达式

switch 可以作为表达式来使用,有自己的值。

switch 可以使用箭头标签。使用箭头标签的 case 分支的执行,不会自动转到下一个分支,相当于添加了 break。

如果 switch 表达式的 case 分支是一个代码块,需要使用 yield 来提供表达式的值。

下面代码是 switch 表达式的使用示例。

代码块
clike
自动换行
复制代码
public class SwitchExpression {

  public String formatGifts(int number) {
    return switch (number) {
      case 0 -> "no gifts";
      case 1 -> "only one gift";
      default -> number + " gifts";
    };
  }
}
复制成功

关于switch表达式的详细介绍,请参考下面的这个视频。


模式匹配

模式匹配中的模式由两个部分组成,分别是模式 predicate 和模式变量。模式 predicate 用来判断模式是否匹配目标对象,而模式变量则负责在匹配成功时,从目标对象中抽取值。

Java 17可以使用的模式包括 instanceof 操作符和 switch 表达式。

下面的代码给出了模式匹配的示例。

代码块
clike
自动换行
复制代码
public class ObjectFormatter {

  public String format(Object input) {
    return switch (input) {
      case null -> "";
      case Number n -> NumberFormat.getNumberInstance().format(n);
      case LocalDateTime t -> t.format(DateTimeFormatter.ISO_DATE_TIME);
      default -> input.toString();
    };
  }
}
复制成功

关于模式匹配的详细信息,请参考下面的这个视频。


文本块

文本块允许在Java源代码中添加多行的字符串常量。

下面的代码给出了文本块的使用示例。

代码块
clike
自动换行
复制代码
public class SimpleTextBlocks {

  private static final String BLOCK1 = """
      <user>
        <id>001</id>
        <name>alex</name>
        <email>alex@example.com</email>
      </user>
      """;

  private static final String BLOCK2 = """
      {
        "id": "001",
        "name": "alex",
        "email": "alex@example.com"
      }
      """;
}
复制成功

关于文本块的详细信息,请参考下面的这个视频。


打包工具 jpacakge

打包工具 jpacakge 可以把Java应用打包成 Windows、macOS 和 Linux上的安装文件。安装之后可以直接运行应用。JDK会被自动打包。jpackage 支持对JDK镜像中的模块进行定制。

关于 jpackage 的详细信息,请参考下面的这个视频。


底层API

JFR(Java Flight Recorder) 事件流API允许以流的方式对JFR事件进行异步处理。传统的使用JFR的方式需要启动录制,让应用运行,停止录制之后得到JRF的事件文件,最后再进行分析。这种方式并不适用实时监控的场景,而JRF事件流API可以解决这个问题。

JVM常量API提供了一套标准的API来描述JVM中常量池中的常量。该API在包 java.lang.constant 中。该API主要是提供给字节代码操纵库来使用的。

隐藏类无法被其他类的字节代码来引用。隐藏类只能通过Java反射API来使用。隐藏类的主要作用是让框架在运行时生成无法被发现的类。隐藏类只与类加载器保持松散的联系。当没有对隐藏类的引用时,该类可以被卸载。这一点与普通类是不同的。


Java标准库

Java标准库也有一些改动。

Socket channel 和 server-socket channel 可以使用 Unix 域 socket。

Socket 和 DatagramSocket API的底层实现进行了重写。

伪随机数生成器的API进行了重构和加强。

新增macOS上基于Metal框架的新的Java 2D API渲染引擎。

新的加密算法:Edwards-Curve Digital Signature。

JVM级别的序列化过滤器的工厂对象,可以根据不同的上下文来创建实际使用的序列化过滤器对象。


JVM

CDS(Class-Data Sharing)可以提升JVM的启动速度。从Java 10,CDS支持对应用中的类进行处理。不过使用应用CDS(AppCDS)的操作比较繁琐。一共需要3个步骤才能完成。从Java 13开始,启动 AppCDS 只需要两步即可。第一步是运行一次应用,JVM会自动相应的CDS归档文件。第二步是使用自动生成的CDS归档文件来启动应用。

NullPointerException的消息会包含具体的为null的位置信息,方便错误调试。

JVM的元空间的内存分配算法进行了修改,降低了类加载器的开销。

新增了ZGC和Shenandoah两种低暂停时间的GC方式。

CMS回收器已经被删除。

G1回收器做了较大的更新。


外部方法和内存API

外部方法和内存API(Foreign Function & Memory API)目前还是孵化功能。这个API提供了新的更安全并且性能更好的方式来访问外部内存,可以替代对 ByteBuffer 和 Unsafe 的使用。同时也支持与 C 原生代码的交互,可以替代 JNI。


关于Java 11到Java 17的升级就介绍到这里。所有这些新特性的介绍都有相关的源代码,见GitHub(VividcodeIO/from-java11-to-java17)。