Pull to refresh

Java 18. Что нового добавили, и почему вам это пригодится

Reading time7 min
Views31K

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

Выход 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, поскольку апгрейд с нее будет простым и удобным.

Tags:
Hubs:
Total votes 48: ↑48 and ↓0+48
Comments33

Articles