Много интересных фич

Выход Java 18 назначен на 22 марта 2022 года, ждать осталось недолго. Релиз уже несколько дней находится во второй фазе стабилизации, а значит, список фич уже финализирован, а значит настало время обратить на них наше внимание.

Важно понимать, что версии, которые выходят между LTS-релизами, в частности помогают опробовать новые возможности и посмотреть, зайдут ли они комьюнити. Бизнесы, если решат обновляться, будут чаще всего ждать полноценной многолетней поддержки. Поэтому в 18-й версии будет несколько JEP в виде превью и инкубаторов, которые разработчики будут тестировать и давать обратную связь. И уже в готовом виде эти улучшения войдут в 22-ю версию Java.

Сейчас у нас есть возможность попробовать новый функционал и понять, насколько он полезен. А пробовать есть что!

В этой статье мы рассмотрим новые JEP и объясним, что они делают, и для чего они нужны. 

Новые фичи в Java 18

JEP 400: Использование UTF-8 по умолчанию

Что это такое: Многие API (включая стандартные Java API) используют в приложениях так называемый “набор символов по умолчанию”. Какой именно 一 зависит от платформы, будь это Windows, Linux, Mac и другие ОС. Более того, та же кодировка использовалась для сохранения исходных .java файлов .

С 18-й Java по умолчанию всегда используется UTF-8.

Зачем это нужно: В некоторых случаях (особенно, когда это касалось азиатских языков и иероглифов), одно и то же приложение, запущенное разными пользователями (даже на одной машине), могло привести к искажению текста. Если виртуальная машина считает кодировкой “по умолчанию” UTF-8, а программа написана в, например, UTF-16, то это может превратить выдаваемый текст в абракадабру, или даже повлиять на работу компилятора javac. 

Об этом, кстати, стоит помнить при миграции с ранних версий Java™, которая, возможно, потребует перекодировки исходных файлов.

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

JEP 408: Простой Web Server

Что это такое: В Java появится легковесный веб-сервер, который можно будет запустить с помощью простой команды $ jwebserver

Зачем это нужно: Для тестирования, прототипирования и обучения. Встроенный сервер, конечно, не предназначен для замены промышленных веб-серверов в конечном продукте. Зато теперь нет необходимости в их настройке только для того, чтобы проверить, работает ли бизнес-логика. Можно программно создавать новые и кастомизировать имеющиеся обработчики адресов.

JEP 413: Кусочки кода в документации Java API

Что это такое: Добавляется новый тег @snippet, который используется в комментариях к коду. Если пометить им часть комментария, то компилятор будет валидировать его и оформлять как если бы это был реальный код.

Зачем это нужно: Для вашего удобства! Код в комментариях может содержать кучу ошибок, связанных с человеческим фактором 一 в первую очередь, опечатками. Конечно, компилятор не сможет превратить плохой код в хороший, но про отсутствующую скобку напомнит. А хорошие читаемые комментарии могут сэкономить часы работы при анализе чужого, да и своего кода. 

JEP 416: Новая реализация Core Reflection через MethodHandle

Что это такое: Механизм рефлексии переписан поверх MethodHandle-ов, которые заменяют генерацию байткода для  Method::invoke, Constructor::newInstance, Field::get и Field::set. 

Зачем это нужно: Для безопасности и скорости, а также разработки project Valhalla. До этого использовалось три механизма java.lang.reflect API: нативные методы виртуальной машины, динамически генерируемые байткод-стабы в сочетании с доступом к полям через Unsafe, и, собственно, method handles. Такое разнообразие сложно поддерживать при добавлении новых фич, таких как примитивные классы и дженерики с поддержкой примитивов.

Теперь механизм с использованием нативных методов используется только на раннем этапе запуска виртуальной машины. Для улучшения производительности рефлексии в горячем коде рекомендуется хранение экземпляров Method, Constructor и Field в static final полях, чтобы JIT оптимизировал их как константы.

JEP 417: Vector API (третий инкубатор)

Что это такое: Vector API, представленный в Java 16, помогает существенно повысить производительность приложений в некоторых случаях. Он позволяет вручную писать платформенно-независимые векторные алгоритмы на Java™, когда существующая автоматическая векторизация циклов HotSpot не срабатывает для сложного кода.

Зачем это нужно: Производительность некоторых вычислений в области, например, финансовых операций, машинного обучения и криптографии. Эта версия API 一 в стадии третьего инкубатора, созданного на основе отзывов сообщества. Она содержит новые функции, включая поддержку инструкций ARM Scalable Vector Extension (SVE) и повышенную эффективность векторных операций, принимающих маски на архитектурах, аппаратно поддерживающих маскирование.

JEP 418: Интерфейс сервис-провайдера для разрешения сетевого адреса

Что это такое: Сейчас для разрешения имен хостов в межсетевом протоколе (IP), в Java™ используется API java.net.InetAddress. Он, в свою очередь, использует механизм разрешения адреса через системный вызов ОС. Обычно он работает с файлом hosts в сочетании с DNS (система доменных имен).

Этот JEP внедряет интерфейс поставщика услуг (SPI) и ряд новых классов, чтобы API java.net.InetAddress мог использовать другие механизмы разрешения адреса.

Зачем это нужно: Для тестирования, кастомизации и реализации DNS на клиенте без удержания потока операционной системы. Это позволяет, например, эффективно использовать проект Loom, в котором обработка сетевых запросов не будет препятствовать одновременной работе множества виртуальных потоков. 

JEP 419: Foreign Function & Memory API (второй инкубатор)

Что это такое: Улучшение фичи, внедренной в более раннюю версию для замены Java Native Interface (JNI) на более продвинутую модель разработки с использованием чисто Java™ кода. Данный JEP позволит подключать нативные библиотеки, вызвать внешние функции и получать более эффективный доступ к памяти за пределами кучи.

В режиме второй инкубации были добавлены новые носители (carriers), такие как boolean и MemoryAddress, упрощенные API для получения downcall MethodHandle-ов (Java→native) и для управления временными зависимостями областей видимости ресурсов. Представлен обобщенный API разыменования для MemorySegment и MemoryAddress.

Зачем это нужно: Для легкого взаимодействия с библиотеками, написанными на других языках. Становится возможным использовать при таком взаимодействии современные идиомы Java, например параллельные стримы при переносе данных.

JEP 420: Pattern Matching для switch (второй preview)

Что это такое: Еще одно обновление уже имеющейся фичи, которая позволяет проверять выражения на соответствие различным образцам с определенным действием для каждого из них. Ранее эта фича была представлена в режиме preview, а сейчас в нее внесены два улучшения. 

Первое заключается в том, что проверка доминирования требует, чтобы константная метка оператора case стояла всегда перед защищенным (guarded) образцом того же типа. Благодаря этому селектор-выражение может соответствовать нескольким меткам в блоке switch. Кроме того, может использоваться любой ссылочный тип. Взгляните на этот пример:

static void error(Object o) {
  switch(o) {
    case CharSequence cs ->
      System.out.println("A sequence of length " + cs.length());
    case String s ->	// Error - pattern is dominated by previous pattern
      System.out.println("A string: " + s);
  }
}

Если метка в блоке switch будет перекрываться меткой, поставленной ранее в данном блоке, это приведет к ошибке во время компиляции. Таким образом, в похожих ситуациях вы можете обнаружить плохо написанный код. Второе улучшение заключается в повышении эффективности проверки блоков switch на предмет полноты. В результате проверка будет более точной в запечатанных (sealed) иерархиях, где разрешенный непосредственный наследник лишь расширяет вариант обобщенного (generic) родительского класса с осуществленной подстановкой типов. Другими словами, компилятор не только помогает поддерживать код в упорядоченном состоянии, но и полностью понимает иерархии sealed классов.

Зачем это нужно: Как и прошлый JEP, этот сделает Java более современной. Старый синтаксис, разработанный 20 лет назад, проигрывает по удобству написания кода в сравнении с новыми языками программирования, и разработчики Java устраняют этот разрыв подобными нововведениями.

JEP 421: Объявление функции финализации устаревшей

Что это такое: Скрывается устаревшая функция финализации, которая была представлена еще в Java 1.0. В дальнейшем она будет полностью удалена.

Зачем это нужно: Финализация применялась для устранения проблем утечки внешних ресурсов, больше не используемых приложением.

Автоматическая система управления памятью должна применять сборщик мусора для того, чтобы находить недоступные объекты и освобождать их память.

На практике при работе с объектами, которые работают с ресурсами не только виртуальной машины, но и скажем операционной системы, (например, файловыми дескрипторами или блоками нативной памяти), возникает следующая проблема: при освобождении объекта связанные с ним ресурсы операционной системы не освобождаются автоматически (нужен дополнительный вызов).

Результатом становится утечка ресурса, который операционная система продолжает считать используемым. 

Для борьбы с этим явлением сначала использовался явный вызов Java-методов, освобождающих внешние ресурсы объектов. Его недостаток - высокая вероятность ошибок. К тому же момент вызова может быть выбран не оптимально. 

Следующим решением стала финализация, целью которой было снять с разработчика задачу гарантированного высвобождения ресурсов. Финализаторы вызываются сборщиком мусора автоматически и гарантированно освобождают ресурсы, относящиеся к неиспользуемым объектам.

На практике финализация столкнулась со множеством проблем в работе. 

  • Время между моментом, когда объект становится недоступным, и вызовом финализатора, могло быть длительным. Весь этот период ресурсы, которые использовал объект, остаются захваченными.

  • Финализатор может сохранять ссылку на финализируемый объект, а это в свою очередь делает возможным его “воскрешение”.

  • Финализатор выполняется всегда, даже когда его работа не требуется, например, при закрытии программы. 

  • Для работы финализаторов используются неконтролируемые потоки, и порядок выполнения финализаторов также не определен.

В результате финализаторы создавали новые проблемы безопасности, замедляли или останавливали работу программы, ненадежно исполнялись и усложняли программирование. Например, ресурсы могли освобождаться медленнее, чем захватывались, что всё равно приводило к их утечке.

Именно поэтому были разработаны альтернативные инструменты, такие как cleaner-ы с явной регистрацией объектов и интерфейс AutoCloseable в сочетании с оператором try-with-resources. 

Процесс перехода займет немало времени, так как многие нативные библиотеки и сам JDK используют финализаторы, которые необходимо убрать из кода. 

Java 18, есть ли повод обновить рантайм?

Java 一 зрелый язык, но подобные нововведения обновляют ее достаточно, чтобы составить конкуренцию по удобству и функциональности более молодым языкам программирования, сохранив все богатство возможностей, мультиплатформенность и обратную совместимость.

Стоят ли они того, чтобы обновить свой рантайм до не-LTS версии? В большинстве случаев 一 нет. Но учитывая новый полугодовой цикл выпуска новых версий и скорое окончание поддержки Java 8, на которой до сих пор работает множество приложений, оно помогает смотреть в будущее с оптимизмом. Выход новой LTS-версии станет большим событием, а разработку новых программ имеет смысл начинать уже на 18-й Java, поскольку апгрейд с нее будет простым и удобным.

Комментарии (29)


  1. Myxach
    08.02.2022 04:41
    +3

    JEP 413: Кусочки кода в документации Java API
    JEP 419: Foreign Function & Memory API (второй инкубатор)
    JEP 420: Pattern Matching для switch (второй preview)
    Вроде все, что интересно. Может кому-то ещё рефлексия зайдет.


    1. olegchir
      08.02.2022 13:44
      +5

      Хей @tagir_valeev а в Идее @сниппеты можно автодополнять? Можно сделать какой-нибудь рефакторинг типа "move to snippet"?


      1. Teapot Автор
        08.02.2022 18:07
        +2

        Тогда уж и запуск сниппета!


        1. Myxach
          09.02.2022 14:56

          и это все при исполнение программы


  1. Enokin
    08.02.2022 10:30
    +8

    Лет через 10, поюзаем в продакшн


    1. tbl
      08.02.2022 17:21
      +2

      Угу, где-то да. До сих пор рекрутеры в личку стучатся с предложениями: "Ищу Java-разработчика в один из крупнейших российских банков. Java 8, 11, настоящий High-load и Микросервисы! Актуальный стек: Struts, Hibernate, Tomcat, ActiveMQ, Maven, ... "


      1. tmin10
        08.02.2022 18:34

        Apache Spark тоже 8 и 11 версии поддерживает, на них и сидим :)


      1. Nik_kosmo
        08.02.2022 22:12

        В каких-то компаниях можно и на 7 поработать


      1. kpmy
        08.02.2022 22:12

        А какой сейчас стек мечты?


      1. mrbald
        09.02.2022 00:46

        Буквально месяц назад апгрейдился, долго думал, выбрал Java 11, она последний LTS на который более-менее переползла экосистема, запускать 6-месячные релизы в prod не круто. Так что нормально, что ищут Java 11. ActiveMQ если правильно настроить (особенно Artemis) тоже вполне себе middleware, пашет и есть не просит.


        1. tbl
          09.02.2022 02:53
          +1

          Java 17 - LTS, уже три патча вышли, скоро следующая мажорная версия выходит, большая часть экосистемных библиотек уже переползли на нее (из того, что используем - spring (boot, webflux, micrometer, spring-data-jdbc), netty, reactor, jackson, guava, log4j2, retrofit, grpc, junit, драйверы к очередям и бд (в том числе и jdbc)). Мы у себя инфраструктурные проекты уже на 17 перевели. Теперь потихоньку внешние сервисы мигрируем с java 11 (слава богу, нет Java EE с легаси-зависимостями)


          1. Teapot Автор
            09.02.2022 11:14
            +1

            Более того, в следующем году уже и очередной LTS релиз выйдет (JDK 21). Актуальный Spring конечно - это сильный довод в пользу 17.

            11 в данный момент тоже хорошо, так как до последнего врмемени портировались в заметных количествах улучшения (в отличие от 8), и как раз перестают, ну и стабилизация тоже активная. То есть в каком-то смысле сейчас самый пик поддержки 11.


  1. tbl
    08.02.2022 10:37

    В switch-выражениях теперь вымораживает возможность указать default -> и case default ->, что выглядит одинаково, но второй является уже вариантом для паттерн-матчинга (позволяя, например, комбинировать так: case default, null ->). Надеюсь, первый вариант синтаксиса задеприкейтят и удалят в будущих версиях в пользу второго.


    1. BugM
      08.02.2022 23:37

      Удалить нельзя ничего. По крайней мере быстрее чем лет за 30 точно.

      new Date() передает привет.


      1. tbl
        08.02.2022 23:47
        +1

        Это синтаксический сахар языка, а не библиотека или байт-код. Совместимость на уровне скопированного кода не нарушится. Просто старые java-файлы перестанут компилироваться без внесения изменений. Это некритично для сохранения обратной совместимости. Уже делали более сильные изменения.


        1. BugM
          08.02.2022 23:52

          Просто старые java-файлы перестанут компилироваться без внесения изменений. Уже делали более сильные изменения.

          Можно пример? Чтобы java код написанный без библиотек перестал компилироваться.


          1. tbl
            09.02.2022 02:37
            +1

            Из того, что помню: добавление ключевых слов assert в 1.4 (сломался junit и код, который его использует), enum в 1.5, запрет на использование символа подчеркивания в качестве идентификатора переменной в java 11. К каждой версии JDK выпускается Migration guide, в котором описываются несовместимые изменения в jls, jvms, рантайм-библиотеке и тулзах, поставляемых с jdk(jre)


            1. BugM
              09.02.2022 03:06
              +2

              добавление ключевых слов assert в 1.4 (сломался junit и код, который его использует

              Библиотеки ломаются время от времени. Я именно про core java.

              enum в 1.5

              Спецификации уже нет. Увы. Таких исторических книжек у меня тоже уже нет.

              https://docs.oracle.com/javase/specs/index.html

              запрет на использование символа подчеркивания в качестве идентификатора переменной в java 11

              An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
              The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII dollar sign ($, or \u0024) and underscore (_, or \u005f). The dollar sign should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems. The underscore may be used in identifiers formed of two or more characters, but it cannot be used as a one-character identifier due to being a keyword.

              https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.8

              Для сравнения 6 https://docs.oracle.com/javase/specs/jls/se6/html/lexical.html#3.8

              К каждой версии JDK выпускается Migration guide, в котором описываются несовместимые изменения в jls, jvms, рантайм-библиотеке и тулзах, поставляемых с jdk(jre)

              Конечно. Но чтобы ломались базовые языковые фичи языка это вам не к Джаве. Тем более после истории третьего Питона. Все теперь совсем умными стали.


              1. tbl
                09.02.2022 03:26
                +1

                То, что вы в ссылках привели, как раз и подтверждает, что язык меняется от версии к версии: добавляются новые ключевые слова, которые нельзя использовать в идентификаторах в java-коде. Некоторые слова наоборот убирают, например, ставший ненужным strictfp (если попытаться скомпилировать код с ним, то, начиная с 17 версии jdk, компиляция будет падать)


                1. BugM
                  09.02.2022 14:23

                  strictfp это отличный пример. Он лет 10 вроде уже ничего не делал? И только сейчас выпилили.

                  Удаление фич с ломанием обратной совместимости это тяжёлый и долгий путь. Ломать семантику switch это история минимум лет на 20. Вероятнее что даже начинать не будут.


                  1. tbl
                    09.02.2022 14:55
                    +1

                    Switch-expression (case blablabla -> ) в java 12 добавили (релиз был в 2019 году). Я не имею ввиду switch-statement (case blablabla: ). Последнее трогать не предлагаю.


                    1. BugM
                      09.02.2022 16:20
                      +1

                      Когда добавили это не важно. Оно попало в LTS релиз. На этом все. Это навсегда или почти навсегда. Быстрее 20 лет точно ничего никто не удалит.


      1. urvanov
        09.02.2022 14:00

        Date, Calendar, Joda-Time, Java Time API - во сколько теперь способов работы с датой и временем


        1. fRoStBiT
          10.02.2022 00:20

          И с Java 8 только один из них правильный


  1. RiverFlow
    09.02.2022 10:56
    -4

    Слава Богу я удрал из этого джава мрака сто лет назад


    1. same_one
      09.02.2022 15:43

      Куда?


  1. andreydelay
    09.02.2022 11:57

    Спасибо за очень интересную статью!)


  1. urvanov
    09.02.2022 14:02

    Фичи не особо, скажем, мегаожидаемые. Думаю, ещё долго на 18 не будут переходить


    1. BugM
      09.02.2022 14:13
      +1

      На неё и не надо переходить. Не LTS же.

      Это версия на посмотреть новые фичи, попробовать. Не для прода.