Выпуск 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 можно отметить ([1], [2], [3], [4], [5]):

  • Добавлена предварительная поддержка строковых шаблонов (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("?", 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.

Источник: http://www.opennet.ru/opennews/art.shtml?num=59787