Что нового в Java 23

Что нового в Java 23

В сентябре ожидается выход новой Java 23, а список JDK Enhancement Proposal (JEP) сформирован и доступен уже сейчас.
Оцените изменения в JDK 23, чтобы заранее продумать миграцию на версию с долгосрочной поддержкой (LTS), потому что JDK 23 не является LTS-версией.

В этой статье вы узнаете какие JEP вошли в новую Java и чего коснутся эти изменения.

Содержание:

Новое

JEP 455: Примитивные типы в шаблонах pattern matching, instanceof и switch (предварительная версия)

Эта функция улучшит pattern matching, позволив использовать примитивные типы во всех контекстах шаблонов, а также расширит функции instanceof и switch для работы со всеми примитивными типами.

Цели этого JEP — устранить риск потери данных из-за небезопасного приведения типов и позволить исследовать данные примитивных или ссылочных типов единым образом. Раньше instanceof работал только со ссылочными типами, и не было способа проверить примитивные значения на безопасность приведения. Из-за этого примитивное значение могло быть тихо преобразовано без выброса исключения. Теперь оператор instanceof проверяет примитивные типы. Если значение может быть безопасно приведено, instanceof вернёт true, иначе — false.

JEP 467: Поддержка Markdown в JavaDoc-комментариях

При написании JavaDoc-комментариев будет использоваться Markdown, а не только сочетание HTML и тегов JavaDoc. Это облегчит чтение и написание комментариев в исходном виде для API. Markdown не заменит HTML и теги JavaDoc, а станет дополнительным способом написания комментариев для документирования.

JEP 476: Импортирование модулей (предварительная версия)

Java 23 позволит импортировать сам модуль вместо явного импортирования его отдельных пакетов. Это упростит переиспользование модульных библиотек путём одновременного импортирования модулей.

Также это сократит число строк в коде для импорта нужных пакетов модуля. Начинающие разработчики смогут импортировать требуемые библиотеки и системные Java-классы без необходимости изучать их положение в иерархии пакетов.

Так, вместо:

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

Или:

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

Достаточно написать:

import module java.base

Улучшения

JEP 466: Class-File API (вторая предварительная версия)

В Java 22 в предварительной версии появилось стандартный API для парсинга, генерации и трансформации class-файлов. Этот API будет развиваться вместе с форматом класс-файлов и позволит компонентам и фреймворкам Java-платформы использовать его вместо сторонних библиотек.

Во второй предварительной версии внедрены следующие улучшения на основе обратной связи:

  • Упрощён класс CodeBuilder.
  • Экземпляры AttributeMapper в Attributes теперь доступны через статические методы, а не через статические поля. Это позволяет реализовать отложенную (“ленивую”) инициализацию и снизить затраты на запуск Java.
  • Переделали Signature.TypeArg в алгебраический тип данных.
  • Добавлены ClassReader.readEntryOrNull и ConstantPool.entryByIndex, которые выбрасывают ConstantPoolException вместо ClassCastException, если запись по индексу не требуемого типа. Процессоры Class-File смогут указывать, что несоответствие типа записи набора констант — это проблема формата Class-File, а не процессора.
  • Класс ClassSignature точнее моделирует общие сигнатуры суперклассов и суперинтерфейсов.
  • Исправлена опечатка в несоответствии имён в TypeKind.
  • Удалены подробные методы реализации в ClassReader.

JEP 469: Vector API (восьмой инкубатор)

Vector API появилось в JDK 16 и пока остаётся в инкубационном периоде. В восьмом инкубаторе всё без изменений.

Vector API использует SIMD-инструкции (Single Instruction, Multiple Data), распараллеливая векторные вычисления.
Vector API остаётся в инкубаторе, потому что зависит от некоторых функциональностей проекта Valhalla, который находится в разработке. Как только этот проект станет доступен в предварительной версии, векторное API так же перейдёт из инкубатора в предварительную версию.

JEP 473: Stream Gatherers (вторая предварительная версия)

Stream Gatherers появились в JDK 22 для усовершенствования Stream API поддержкой пользовательских intermediate-операторов. Во второй предварительной версии нет изменений по сравнению с JDK 22, цель этой версии — собрать дополнительную обратную связь и улучшить опыт использования.

Stream Gatherers позволит стримам преобразовывать данные более лёгкими способами по сравнению с существующими встроенными intermediate-операторами. Цели создания Stream Gatherers — сделать стримы более гибкими и позволить пользовательским intermediate-операторам управлять бесконечными потоками. До Stream Gatherers для создания необходимой логики в уже существующих intermediate-операторах приходилось создавать отдельные классы или методы. Теперь вместо этого введён Stream::gather(Gatherer), который позволяет обрабатывать потоки в определённом пользователем порядке.

JEP 474: Сборщик мусора ZGC по умолчанию — generational

Сборщик мусора Z Garbage Collector (ZGC) появился ещё в Java 21. В Java 23 non-generational признан устаревшим и будет удалён в следующей версии. Цели этого JEP — снизить затраты на поддержку двух сборщиков мусора ZGC: non-generational и generational и предупредить о дальнейшем фокусе разработки на generational.

Раньше ZGC хранил молодые и старые объекты вместе и собирал все объекты сразу. С созданием generational, ZGC хранит молодые и старые объекты в разных областях и чаще собирает молодые объекты, что уменьшает потребление ресурсов CPU и кучи при сборке мусора.

JEP 477: Неявные классы и main-методы экземпляра класса (третья предварительная версия)

Эта функциональность была включена в JDK 21. С её помощью начинающие Java-разработчики могут писать простые программы без сложных конструкций корпоративной разработки, а затем усложнять свои программы по мере углубления знаний. Опытные разработчики смогут писать лаконичные приложения без компонентов, используемых для программирования крупных систем.

Так выглядит базовая программа Hello World!, которая изучается новичками в Java:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Для простой по сути программы на Java здесь много кода, непонятных новичку понятий и конструкций: class с модификатором доступа public, параметр String[] args, модификатор static и System.out.println.

Неявные классы имеют только конструктор zero-parameter по умолчанию, находящийся в пакете unnamed, на который нельзя ссылаться по имени. Каждый неявный класс должен содержать метод main и представлять собой самостоятельную программу.

Тогда Hello World! упрощается до трёх строк:

void main() {
    System.out.println("Hello, World!");
}

В третьей предварительной версии добавилось следующее:

  • Неявные классы автоматически импортируют три статических метода для простого текстового ввода и вывода с консоли. Эти методы объявлены в новом классе верхнего уровня java.io.IO:

    • public static void println(Object obj),
    • public static void print(Object obj),
    • public static String readln(String prompt).

Теперь можно обойтись без System.in и System.out и связанных с ними понятий, усложняющих понимание в начале изучения Java. Так, простая интерактивная программа упрощается до:

void main() {
    String name = readln("Введите своё имя: ");
    print("Приятно познакомиться, ");
    println(name);
}
  • Неявные классы автоматически по требованию импортируют все публичные классы верхнего уровня и интерфейсы пакетов, экспортируемых модулем java.base. Станет возможным использование API из широко используемых пакетов в теле неявного класса, как если бы эти пакеты были импортированы.

JEP 480: Структурированный параллелизм (третья предварительная версия)

Чтобы упростить многопоточное программирование, в JDK 19 было представлено API структурированного параллелизма. Это третья предварительная версия этого API, которая будет включена в JDK 23 без изменений для получения обратной связи от разработчиков.

Структурированный параллелизм рассматривает группы связанных задач, выполняемых в разных потоках, как единое целое. Задача расщепляется на несколько подзадач, которые выполняются в отдельных потоках. Затем эти подзадачи воссоединяются в блоке кода главной задачи. Таким образом, задача координирует выполнение подзадач и отслеживать ошибки их выполнения. Это устраняет риски, связанные с отменой и внезапным завершением работы, и повышает надёжность многопоточных приложений.

  • Взаимосвязь между задачами и подзадачами будет чётко прослеживаться в структуре кода, что повышает наблюдаемость.
  • Если происходит сбой в подзадаче или поток, выполняющий задачи, прерывается до join(), то другие подзадачи отменяются. Это повышает надёжность кода.

JEP 481: Scoped Values (третья предварительная версия)

Scoped values впервые появились в JDK 20 в режиме инкубатора. В третьей предварительной версии внесено одно изменение: метод ScopedValue.getWhere будет удалён. Этот метод заменит новый функциональный интерфейс, позволяющий JVM понять, будет ли выброшено исключение.

Scoped values позволяют расшаривать неизменяемые данные в рамках одного потока и между потоками. Scoped values следует использовать вместо локальных переменных потока (thread locals), поскольку они уменьшают сложность кода и повышают надёжность многопоточных приложений.

JEP 482: Гибкий конструктор (вторая предварительная версия)

Эта функциональность появилась в JDK 22 и называлась JEP 447: Выражения перед super(…). Чтобы уменьшить объём и сложность кода, можно добавлять инструкции в конструкторе перед явным вызовом конструктора super() или this(). Это не нарушает естественный порядок инициализации сверху-вниз (top-down).

Во второй предварительной версии изменилось следующее: конструктор сможет инициализировать поля в том же классе перед явным вызовом конструктора. Так конструктор суперкласса (superclass) не выполнит код, который увидит значение поля по умолчанию подкласса (например, 0, false или null). Это может произойти, когда из-за переопределения конструктор суперкласса вызывает метод в подклассе, используя некоторое поле.

Устаревшее (deprecated)

JEP 471: Подготовка к удалению методов доступа к памяти в sun.misc.Unsafe

Цель создания класса sun.misc.Unsafe — выполнение низкоуровневых операций в JDK, поскольку этот класс содержит методы для доступа к памяти on-heap и off-heap. Эти методы могут помочь увеличить производительность в некоторых специфических сценариях, но только в том случае, если по пути выполняются исчерпывающие проверки на безопасность. Иначе использование этих методов может привести к неожиданному поведению приложения, сбоям JVM или снижению производительности. Многие библиотеки используют sun.misc.Unsafe, но не все выполняют требуемые проверки безопасности.

Для решения этой проблемы введены два запасных API: Variable Handles для доступа к памяти on-heap и Foreign Function & Memory API для работы с памятью off-heap.

С помощью этих API стало возможным вывести sun.misc.Unsafe из использования с помощью JEP 471 и удалить этот класс в будущих выпусках.

Переходите на Axiom JDK Pro с улучшенными функциями безопасности и поддержкой отечественных инженеров

Axiom JDK поддерживает все LTS-релизы Java (8, 11, 17, 21), а также текущий релиз. Мигрируйте на любую версию Axiom JDK Pro и получайте ежеквартальные обновления безопасности, экстренные патчи и доступ к возможностям для российских разработчиков:

  • доверенному репозиторию Java-библиотек,
  • Axiom JDK Certified, сертифицированному ФСТЭК по 4 ОУД.
  • Axiom JDK Express, который улучшает пропускную способность, время отклика и время запуска проектов на базе JDK 8 или 11 за счёт виртуальной машины JVM 17.

Свяжитесь с нами, наши инженеры расскажут о продуктах Axiom JDK, предоставят демо-версию и помогут с миграцией.

Вступайте в наш Telegram-канал, чтобы быть в курсе новостей мира Java.

Author image

Сергей Лунегов

Директор по продуктам Axiom JDK

Axiom JDK info@axiomjdk.ru Axiom JDK logo Axiom Committed to Freedom 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67 Axiom JDK 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67