Вышла 16-я версия платформы Java SE. В этот релиз попало около двух с половиной тысяч закрытых задач и 17 JEP'ов. Изменения API можно посмотреть здесь. Release notes здесь.
Уже сейчас доступны для скачивания дистрибутивы Oracle JDK и OpenJDK.
JEP'ы, которые попали в Java 16, мы разобьём на четыре категории: язык, API, JVM и инфраструктура.
Язык
Паттерн-матчинг для оператора instanceof
(JEP 375)
Оператор instanceof
с паттерн-матчингом, который появился в Java 14 и перешёл во второе preview в Java 15, теперь стал стабильной синтаксической конструкцией и больше не требует флага --enable-preview
. Паттерн-матчинг мы подробно рассматривали в этой статье, и с того момента в него было внесено два изменения:
Во-первых, переменные паттернов теперь не являются неявно финальными:
if (obj instanceof String s) { s = "Hello"; // OK в Java 16, ошибка в Java 15 }
Во-вторых, если тип выражения, известный на этапе компиляции, является подтипом проверяемого типа, то теперь это ошибка компиляции:
String str = ... if (str instanceof String s) { // Oшибка в Java 16, OK в Java 15 }
Записи (JEP 395)
Ещё одна синтаксическая конструкция, которая стала стабильной – это записи. Она также была в режиме preview в Java 14 и Java 15. Записи мы также подробно рассматривали ранее. В Java 16 было внесено следующее изменение: теперь во внутренних классах разрешено объявлять статические члены:
public class Outer { public class Inner { // OK в Java 16, ошибка в Java 15 static void main(String[] args) { } // OK в Java 16, ошибка в Java 15 record Point(int x, int y) { } } }
sealed
классы (второе preview) (JEP 397)
«Запечатанные» классы, которые появились в Java 15 в режиме preview, остаются в этом статусе. Их мы рассматривали в этой статье. Изменения по сравнению с прошлой версией следующие:
- Теперь в спецификации языка Java появилось понятие contextual keyword взамен старым понятиям restricted keyword и restricted identifier, и одними из таких contextual keywords стали
sealed
,non-sealed
иpermits
. - Компилятор теперь производит более строгие проверки при конверсии типов, в иерархиях которых есть
sealed
классы:sealed interface Sealed { } final class Impl implements Sealed { void f(Runnable r) { Sealed s = (Sealed) r; // error: incompatible types } }
- Метод
Class.permittedSubclasses()
переименован вClass.getPermittedSubclasses()
.
JVM
Строгая инкапсуляция внутренностей JDK по умолчанию (JEP 396)
Инкапсуляция внутренних API JDK, которая была введена в Java 9, теперь стала строгой: если в Java 9-15 значение опции --illegal-access
было по умолчанию permit
, то с Java 16 она становится deny
. Это значит, что рефлективный доступ к защищённым членам классов и статический доступ к неэкспортированным API (sun.*
, com.sun.*
, jdk.internal.*
и т.д.) теперь будет выбрасывать ошибку.
Если код требует доступа к внутренностям JDK во время выполнения, то чтобы он продолжал работать на Java 16, теперь придётся явно указывать одну из трёх опций JVM:
--illegal-access=permit/warn/debug
: открытие всех пакетов JDK--add-opens=module/package=target-module
: открытие одного пакета--add-exports=module/package=target-module
: экспортирование одного пакета (только для статического доступа)
В будущем опция --illegal-access
может быть удалена окончательно. Начиная с Java 16, при её использовании выдаётся предупреждение: Option --illegal-access is deprecated and will be removed in a future release
.
Изменения не касаются критического API в модуле jdk.unsupported
: классы в пакетах sun.misc
и sun.reflect
остаются доступными без флагов.
Warnings for Value-Based Classes (JEP 390)
Классы-обёртки примитивных типов (Integer
, Double
, Character
и т.д.) теперь относятся к категории value-based классов, и их конструкторы, которые ранее стали deprecated в Java 9, теперь помечены как deprecated for removal.
Понятие value-based классов появилось в спецификации API Java 8. Такие классы являются неизменяемыми, создаются только через фабрики, и в их использовании не должны использоваться операции, чувствительные к identity: сравнение на ==
, синхронизация, identityHashCode()
и т.д. Value-based классы являются кандидатами для миграции на примитивные классы в рамках проекта Valhalla, который сейчас находится в стадии активной разработки.
При синхронизации на объектах value-based классов теперь будет выдаваться предупреждение во время компиляции:
Double d = 0.0; synchronized (d) { // warning: [synchronization] attempt to synchronize on an instance of a value-based class }
Также можно включить проверки синхронизации на value-based объектах во время выполнения с помощью флагов JVM:
-XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=1
: при попытке синхронизации будет фатальная ошибка.-XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2
: при попытке синхронизации будет предупреждение.
ZGC: Concurrent Thread-Stack Processing (JEP 376)
Обработка стеков потоков в сборщике мусора ZGC теперь перенесена из safepoints в конкурентную фазу. Это позволило ещё сильнее уменьшить паузы сборщика мусора.
Unix-Domain Socket Channels (JEP 380)
Добавлена поддержка сокетов доменов Unix в socket channel и server-socket channel API. Такие сокеты используются для межпроцессного взаимодействия внутри одного хоста, и в них не используются сетевые соединения, что делает такое взаимодействие более безопасным и эффективным. Сокеты доменов Unix с недавних пор поддерживаются в Windows 10 и Windows Server 2019.
Elastic Metaspace (JEP 387)
Metaspace (пространство JVM, в котором хранятся метаданные классов) переработан для более эффективной отдачи неиспользуемой памяти обратно операционной системе и меньшего потребления памяти вне кучи в целом. Такое улучшение может быть полезно для приложений, которые интенсивно загружают и выгружают классы посредством большого количества загрузчиков классов.
Alpine Linux Port (JEP 386)
JDK теперь портирован на Alpine Linux и другие дистрибутивы Linux, которые используют musl в качестве реализации стандартной библиотеки C. Alpine Linux популярен в облаках, микросервисах и контейнерах благодаря своему маленькому размеру образа. Новый порт позволит нативно запускать JDK в этих окружениях.
Windows/AArch64 Port (JEP 388)
JDK также портирован на архитектуру Windows/AArch64. Это позволит запускать Java на компьютерах с Windows on ARM, которые в последнее время набирают популярность.
API
Новые методы в Stream
Хотя для этих двух новых методов в интерфейсе java.util.stream.Stream
нет отдельного JEP, хочется упомянуть их здесь, так как это довольно заметное изменение.
Первый метод – это Stream.toList()
. Этот метод собирает содержимое Stream
в неизменяемый список и возвращает его. При этом, в отличие от Collectors.toUnmodifiableList()
, список, который возвращается из Stream.toList()
, толерантен к null
-элементам.
Второй метод – это Stream.mapMulti()
(и примитивные специализации). Это метод является императивным аналогом метода Stream.flatMap()
: если flatMap()
принимает функцию, которая для каждого элемента должна вернуть Stream
, то mapMulti()
принимает процедуру с двумя параметрами, где первый параметр – это текущий элемент, а второй – Consumer, в который кладутся значения. Пример:
IntStream.rangeClosed(1, 10).mapMulti((i, consumer) -> { for (int j = 1; j <= i; j++) { consumer.accept(j); } }); // Возвращает 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, ...
Инструмент упаковки (JEP 392)
Инструмент создания самодостаточных приложений jpackage
, который появился в Java 14 в инкубационном статусе, теперь стал постоянным модулем.
Vector API (Incubator) (JEP 338)
Появился новый инструментарий для преобразования векторных вычислений в SIMD-инструкции процессора (x64 и AArch64). Векторное API позволит разработчику контролировать процесс компиляции и не полагаться на автовекторизацию, которая в JVM является ограниченным и хрупким механизмом. Явная векторизация может применяться в таких областях как машинное обучение, линейная алгебра, криптография и др.
API находится в инкубационном модуле jdk.incubator.vector
.
Foreign Linker API (Incubator) (JEP 389)
Ещё одно новое API, которое появилось в результате работы над проектом Panama – это Foreign Linker API. Это инструментарий для статического доступа к нативному коду из Java, созданный для замены JNI: он должен быть более простым в использовании, более безопасным и желательно более быстрым.
Про Foreign API делал доклад Владимир Иванов из Oracle.
Foreign-Memory Access API (Third Incubator) (JEP 393)
API для доступа вне кучи Java, которое появилось в Java 14, остаётся в инкубационном статусе с некоторыми изменениями.
Инфраструктура
Enable C++14 Language Features (JEP 347)
Кодовая база JDK до Java 16 использовала стандарты C++98/03. При этом с Java 11 код стал собираться версией с более новым стандартом, однако в нём всё ещё нельзя было использовать возможности стандарта C++11/14. Теперь же часть из этих возможностей использовать можно: в гиде по стилю HotSpot определён список возможностей C++11/14, которые можно использовать и которые нельзя.
Migrate from Mercurial to Git (JEP 357) и Migrate to GitHub (JEP 369)
Совершён переход репозиториев JDK на Git и GitHub. Миграция была полностью завершена в сентябре 2020 года, и разработка Java 16 уже полностью велась в новом репозитории.
Переход на GitHub облегчил процесс принятия изменений контрибьюторами. Теперь изменения предлагаются через привычные большинству пользователей пулл-реквесты, и большая часть процесса автоматизирована с помощью команд и ботов. Подробнее про процесс можно прочитать на странице проекта Skara.
Также сейчас обсуждается переход на Git более старых версий JDK: jdk11u и, возможно, jdk8u.
Java 16 является STS-релизом, у которого выйдет только два обновления.