После шести месяцев разработки компания Oracle выпустила платформу Java SE 21 (Java Platform, Standard Edition 21), в качестве эталонной реализации которой используется открытый проект OpenJDK. За исключением удаления некоторых устаревших возможностей в Java SE 21 сохранена обратная совместимость с прошлыми выпусками платформы Java – большинство ранее написанных Java-проектов без изменений будут работоспособны при запуске под управлением новой версии. Готовые для установки сборки Java SE 21 (JDK, JRE и Server JRE) подготовлены для Linux (x86_64, AArch64), Windows (x86_64) и macOS (x86_64, AArch64). Разработанная в рамках проекта OpenJDK эталонная реализация Java 21 полностью открыта под лицензией GPLv2 с исключениями GNU ClassPath, разрешающими динамическое связывание с коммерческими продуктами.
Java SE 21 отнесён к категории выпусков с расширенным сроком поддержки, обновления для которого будут выпускаться до 2031 года (общедоступные обновления будут выходить до сентября 2028 года). В качестве ветки с длительным сроком поддержки (LTS) также продолжает сопровождаться Java SE 17, обновления для которой будут выпускаться до 2029 года (общедоступные – до 2026 года).
Общедоступная поддержка LTS-ветки Java SE 11 прекращается в сентябре этого года, но расширенная поддержка будет производиться до 2032 года. Расширенная поддержка LTS-ветки ava SE 8 продлится до 2030 года.
Напомним, что начиная с выпуска Java 10 проект перешёл на новый процесс разработки, подразумевающий более короткий цикл формирования новых релизов. Новая функциональность теперь развивается в одной постоянно обновляемой master-ветке, в которую включаются уже готовые изменения и от которой раз в шесть месяцев ответвляются ветки для стабилизации новых выпусков.
Из новшеств Java 21 можно отметить:
- Добавлена предварительная поддержка строковых шаблонов (String Template), реализованных в дополнение к строковым литералам и блокам текста. Строковые шаблоны позволяют совмещать текст с вычисляемыми выражениями и переменными без использования оператора “+”. Подстановка выражений осуществляется при помощи операции {..}, при этом для проверки корректности подставляемых значений могут подключаться специальные обработчики. Например, обработчик SQL обеспечивает проверку значений, подставляемых в SQL-код, и возвращает на выходе объект java.sql.Statement, а обработчик JSON отслеживает корректность подстановок JSON и возвращает JsonNode. String query = “SELECT * FROM Person p WHERE p.” + property + ” = ‘” + value + “‘”; // было Statement query = SQL.”””SELECT * FROM Person p WHERE p.{property} = ‘{value}'”””; // стало
- Добавлена поддержка упорядоченных коллекций (SequencedCollection), предоставляющих методы addFirst, addLast, getFirst, getLast, removeFirst и removeLast для прямого доступа к первым и последним элементам коллекции с постоянным следованием элементов. Упорядоченные коллекции применимы к спискам, sets-наборам (например, TreeSet) и некоторым другим структурам данных. var letters = List.of(“c”, “b”, “a”); “c”.equals(letters.getFirst()); “a”.equals(letters.getLast());
- Реализован генеративный вариант сборщика мусора ZGC (Generational Z Garbage Collector), вводящий раздельную обработку “старых” и “молодых” объектов, что повышает эффективной очистки недавно созданных объектов с небольшим временем жизни. Отмечается, что применение Generational ZGC снижает риски приостановок во время выделения ресурсов, снижает нагрузку на CPU и потребление памяти при сборке мусора. Применение Generational ZGC с Apache Cassandra 4 привело к увеличению пропускной способности в 4 раза при фиксированном размере кучи (heap) и уменьшение размера кучи на четверть при неизменной пропускной способности. Для включения нового режима предложена опция “-XX:+UseZGC -XX:+ZGenerational”.
- Стабилизирована реализация шаблонов записей (record pattern), расширяющая появившуюся в Java 16 возможность сопоставления с образцом средствами для разбора значений классов типа record. Например: record Point(int x, int y) {} static void printSum(Object obj) { if (obj instanceof Point p) { int x = p.x(); int y = p.y(); System.out.println(x+y); } }
- Стабилизирована поддержка сопоставления по шаблону в выражениях “switch”, позволяющая в метках “case” использовать не точные значения, а гибкие шаблоны, охватывающие сразу серию значений, для которых ранее приходилось использовать громоздкие цепочки выражений “if…else”. static String formatterPatternSwitch(Object obj) { return switch (obj) { 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(); }; }
- Стабилизирована реализация виртуальных потоков, представляющих собой легковесные потоки, значительно упрощающие написание и сопровождение высокопроизводительных многопоточных приложений.
- Предложена третья предварительная реализация API FFM (Foreign Function & Memory), позволяющего организовать взаимодействие Java-программ с внешними кодом и данными через вызов функций из внешних библиотек и доступ к памяти вне JVM.
- Добавлена предварительная поддержка безымянных переменных и сопоставлений с шаблоном – вместо неиспользуемых, но необходимых при вызове переменных и шаблонов, теперь можно указывать символ “_”. // было String pageName = switch (page) { case GitHubIssuePage(var url, var content, var links, int issueNumber) -> “ISSUE #” + issueNumber; … }; // теперь можно String pageName = switch (page) { case GitHubIssuePage(_, _, _, int issueNumber) -> “ISSUE #” + issueNumber; };
- Добавлена предварительная поддержка безымянных классов и безымянных экземпляров метода “main”, в которых можно обойтись без объявлений public/static, передачи массива аргументов и прочих сущностей, связанных с объявлением класса. Например, минимальный вариант Java-программы теперь может выглядеть так: // было public class HelloWorld { public static void main(String[] args) { System.out.println(“Hello world!”); } } // теперь можно void main() { System.out.println(“Hello, World!”); }
- Добавлена предварительная поддержка ограниченных значений (Scoped Values), позволяющих совместно использовать неизменяемые данные в потоках и эффективно обмениваться данными между дочерними потоками (значения наследуются). Scoped Values развиваются для замены механизма переменных локальных к потоку (thread-local variables) и более эффективны при использовании очень большого числа виртуальных потоков (тысячи и миллионы потоков). Главное отличие Scoped Values от переменных локальных к потоку в том, что первые записываются один раз, в дальнейшем не могут быть изменены и остаются доступны только на время выполнения потока. class Server { final static ScopedValue CURRENT_USER = new ScopedValue(); void serve(Request request, Response response) { var level = (request. isAuthorized()? ADMIN : GUEST); var user = new User(level); ScopedValue.where(CURRENT_USER, user) .run(() -> Application.handle(request, response)); } } class DatabaseManager { DBConnection open() { var user = Server.CURRENT_USER.get(); if (!user.canOpen()) throw new InvalidUserException(); return new DBConnection(…); } }
- Добавлена шестая предварительная реализация API Vector, предоставляющего функции для векторных вычислений, которые выполняются с использованием векторных инструкций процессоров x86_64 и AArch64 и позволяют одновременно применить операции сразу к нескольким значениям (SIMD). В отличие от предоставляемых в JIT-компиляторе HotSpot возможностей по автовекторизации скалярных операций, новый API даёт возможность явно управлять векторизацией для параллельной обработки данных.
- Добавлен экспериментальный API для cтруктурированного параллелизма (Structured Concurrency), упрощающий разработку многопоточных приложений за счёт обработки нескольких задач, выполняемых в разных потоках, как единого блока.
- Добавлены новые методы: Math.clamp(), StrictMath.clamp(), String indexOf(int,int,int), indexOf(String,int,int), String splitWithDelimiters().
- Добавлены методы для определения свойств emoji: isEmoji(int codePoint), isEmojiPresentation(int codePoint), isEmojiModifier(int codePoint), isEmojiModifierBase(int codePoint), isEmojiComponent(int codePoint) и isExtendedPictographic(int codePoint). var codePoint = Character.codePointAt(“😃”, 0); var isEmoji = Character.isEmoji(codePoint); System.out.println(“😃 is an emoji: ” + isEmoji);
- Добавлен API для использования механизмов инкапсуляции ключей шифрования (KEM, Key Encapsulation Mechanism),
предназначенных для защиты ключей симметричного шифрования при помощи алгоритмов на основе открытых ключей. - Добавлена поддержка алгоритма цифровых подписей HSS/LMS.
- Началась подготовка к запрету динамической загрузки агентов. При попытке динамической загрузки Java-агентов в работающую виртуальную машину JVM теперь будет выводиться предупреждение.
- Прекращена поддержка 32-разрядной платформы Windows.
Дополнительно можно отметить публикацию обновления платформы для создания приложений с графическим интерфейсом JavaFX 21.