Выпуск Java SE 21
После шести месяцев разработки компания 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, передачи массива аргументов и прочих сущностей, связанных с объявлением класса.
// было 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‹user› 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("
© OpenNet