Привет, Хабр! Представляю вашему вниманию перевод статьи «90 New Features (and APIs) in JDK 11» от автора Simon Ritter.
Новый шестимесячный релизный цикл JDK для многих означает, что некоторые ещё даже не выяснили, какие новые функции в JDK 10, а на пороге уже JDK 11. В одном из ранних блогов (англ.), были перечислены все 109 новых фич и API, которые удалось найти в JDK 10. Поэтому для JDK 11 было решено поступить аналогично. Тем не менее, был выбран другой формат. Этот пост будет поделён на два раздела: новые фичи, которые доступны разработчикам (публичный API) и всё остальное. Таким образом, если вас интересует только то, что непосредственно повлияет на вашу разработку, вы можете пропустить вторую часть.
Общее число изменений, которое удалось подсчитать, получилось равным 90 (это JEP плюс новые классы и методы, исключая отдельные методы для HTTP-клиента и Flight Recorder) (прим. переводчика: Java Flight Recorder (JFR) был одним из коммерческих дополнений от Оракла встроенным в JDK, но начиная с Java 11, благодаря JEP 328, был передан в опенсорс). Хоть и в JDK 11 удалось найти на одиннадцать изменений меньше, чем в JDK 10, считаю, что справедливо сказать, что в JDK 11 добавлено больше функциональных возможностей, однозначно на уровне JVM.
В JDK 11 довольно мало изменений, которые могли бы повлияет на стиль разработки. Присутствует небольшое изменение синтаксиса, множество новых API и возможность запуска приложений одним файлом без использования компилятора (прим. переводчика: так называемые shebang файлы). Кроме того, большим (и ломающим) изменением является удаление агрегирующего модуля java.se.ee, что может повлиять на перенос существующего приложения на JDK 11.
JEP 323: Local-Variable Syntax for Lambda Parameters
В JDK 10 был введен вывод локальных переменных (или вывод типов) (JEP 286). Это упрощает код, поскольку вам больше не нужно явно указывать тип локальной переменной, вместо этого можно использовать var. JEP 323 расширяет использование этого синтаксиса, который теперь также применим к параметрам лямбда-выражений. Простой пример:
list.stream()
.map((var s) -> s.toLowerCase())
.collect(Collectors.toList());
Внимательный программист на Java указал бы, что лямбда-выражения уже имеют вывод типа, поэтому использование var было бы (в данном случае) излишним. Мы могли бы так же легко написать тот же код, что и:
list.stream()
.map(s -> s.toLowerCase())
.collect(Collectors.toList());
Зачем было нужно добавлять поддержку var? Ответом является один особый случай — когда вы хотите добавить аннотацию к лямбда-параметру. Это невозможно сделать без участия какого-либо типа. Чтобы избежать использования явного типа, мы можем использовать var для упрощения вещей, таким образом:
list.stream()
.map((@Notnull var s) -> s.toLowerCase())
.collect(Collectors.toList());
Это изменение потребовало изменений в спецификации языка Java (JLS), в частности:
Страница 24: The description of the var special identifier.
Страница 627-630: Lambda parameters
Страница 636: Runtime evaluation of Lambda expressions
Страница 746: Lambda syntax
JEP 330: Launch Single-File Source-Code Programs
Одним из критических замечаний к Java является избыточность синтаксиса, а «церемония», связанная с запуском даже тривиального приложения, может серьёзно повысить порог входа для новичка. Для написания приложения, которое просто печатает «Hello World!», требуется написать класс с общедоступным статическим void main методом и использовать метод System.out.println(). Сделав это, вы должны скомпилировать код с помощью javac. Наконец, вы cможете запустить приложение, которое будет приветствовать мир. Выполнение того же самого сценария, в большинстве современных языков значительно проще и быстрее.
JEP 330 устраняет необходимость компиляции однофайлового приложения. Теперь достаточно ввести:
java HelloWorld.java
Java лаунчер идентифицирует, что файл содержит исходный код Java и скомпилирует код в *.class файл перед его выполнением.
Аргументы, помещенные после имени исходного файла, передаются в качестве аргументов при запуске приложения. Аргументы, помещенные перед именем исходного файла, передаются в качестве аргументов java лаунчеру после компиляции кода (это позволяет задавать такие вещи, как classpath, в командной строке). Аргументы, относящиеся к компилятору (например, путь к классам), также будут переданы в javac для компиляции.
Пример:
java -classpath /home/foo/java Hello.java Bonjour
Будет эквивалентно:
javac -classpath /home/foo/java Hello.java
java -classpath /home/foo/java Hello Bonjour
Этот JEP также обеспечивает поддержку «shebang» файлов. Чтобы уменьшить необходимость даже упоминать java лаунчер в командной строке, можно его включить в первую строку исходного файла. Например:
#!/usr/bin/java --source 11
public class HelloWorld {
...
Флаг -source с используемой версией Java является обязательным.
JEP 321: HTTP Client (Standard)
JDK 9 предоставила новый API для поддержки протокола HTTP Client (JEP 110). Так как JDK 9 предоставила Java Platform Module System (JPMS), этот API был включен как модуль инкубатора. Модули инкубаторов предназначены для предоставления новых API, но не превращают их в стандарт Java SE. Разработчики могут попробовать API, предоставив обратную связь. После внесения необходимых изменений (этот API был обновлен в JDK 10), API можно перенести в основной модуль, чтобы стать частью стандарта.
API HTTP Client теперь является частью стандарта Java SE 11. Это вводит новый модуль и пакет для JDK, java.net.http. Основные классы:
API можно использовать синхронно или асинхронно. В асинхронном режиме используются CompletionFutures и CompletionStages.
JEP 320: Remove The Java EE and CORBA Modules
С введением JPMS в JDK 9 можно было разделить монолитный файл rt.jar на несколько модулей. Дополнительным преимуществом JPMS является то, что теперь можно создать среду выполнения Java, которая включает только модули, необходимые для вашего приложения, значительно уменьшая общий размер. Имея явно определенные границы, устаревшие модули теперь проще удалять из Java API. Это то, что делает этот JEP; метамодуль java.se.ee включает в себя шесть модулей, которые больше не будут частью стандарта Java SE 11 и не будут включены в JDK.
Удалённые модули:
Эти модули были помечены устаревшими (@Deprecated) начиная с JDK 9 и не были включены по умолчанию в компиляцию или рантайм. Если вы пытались скомпилировать или запустить приложение, использующее API из этих модулей на JDK 9 или JDK 10, то потерпели бы неудачу. Если вы используете API из этих модулей в своем коде, вам нужно будет предоставить их в виде отдельного модуля или библиотеки. Судя по отзывам кажется, что модули java.xml, которые являются частью поддержки веб-сервисов JAX-WS, SOAP, — это те, которые вызовут наибольшее количество проблем.
Многие новые API-интерфейсы в JDK 11 являются результатом того, что клиентский модуль HTTP теперь является частью стандарта, а также включение Flight Recorder.
Полный схематичный список изменений API, включая сравнение различных версий JDK, можно посмотреть здесь.
Здесь перечислены все новые методы, отличные от тех, которые содержатся в модулях java.net.http и jdk.jfr. Также не перечислены новые методы и классы в модулях java.security, которые довольно специфичны для изменений JEP 324 и JEP 329 (есть шесть новых классов и восемь новых методов).
Два новых конструктора, которые позволяют указать Charset.
Четыре новых конструктора, которые позволяют указать Charset.
Здесь нет новых методов здесь, но стоит упомянуть, что метод runFinalizersOnExit() теперь удален из обоих классов (может возникнуть проблема при миграции на JDK 11).
Я думаю, что это один из основных моментов новых API в JDK 11. Здесь есть несколько полезных новых методов.
Скорее всего, вы посмотрите на strip() и спросите: «Как это отличается от существующего метода trim()?» Ответ заключается в разнице определения пробелов. (прим. переводчика: если коротко, strip() лучше понимает юникод, подробный разбор на StackOverflow)
Оба этих класса имеют новый метод compareTo(), который принимает StringBuffer/StringBuilder и возвращает int. Метод лексического сравнения аналогичен новому методу compareTo() в CharSequence.
Никаких новых методов. Методы destroy() и stop(Throwable) были удалены. Метод stop(), который не принимает аргументов, все еще присутствует. Может привести к проблеме совместимости.
Все эти классы теперь имеют метод mismatch(), который находит и возвращает относительный индекс первого несоответствия между этим буфером и переданным буфером.
Это один из моих любимых новых API в JDK 11. В качестве примера вы можете преобразовать этот код:
lines.stream()
.filter(s -> !s.isBlank())
в
lines.stream()
.filter(Predicate.not(String::isBlank))
или если мы используем статический импорт:
lines.stream()
.filter(not(String::isBlank))
Лично я считаю, что эта версия более понятна и лаконична.
int deflate(ByteBuffer): сжимает входные данные и заполняет ими указанный буфер.
int deflate(ByteBuffer, int): сжимает входные данные и заполняет ими указанный буфер. Возвращает фактическое количество сжатых данных.
void setDictionary(ByteBuffer): Устанавливает заданный словарь для сжатия в байты в данном буфере. Это перегруженная форма существующего метода, который теперь может принимать ByteBuffer, а не байтовый массив.
void setInput(ByteBuffer): Устанавливает входные данные для сжатия. Также перегруженная форма существующего метода.
Это новый класс в JDK 11. Используется для поддержки запроса на диалог печати или настройки страницы. Должен отображаться поверх всех окон или определенного окна.
JEP 181: Nest-Based Access Control
Java (и другие языки) поддерживает вложенные классы через внутренние классы. Для правильной работы компилятор должен выполнять некоторые трюки. Например:
public class Outer {
private int outerInt;
class Inner {
public void printOuterInt() {
System.out.println("Outer int = " + outerInt);
}
}
}
Компилятор модифицирует это, чтобы создать что-то вроде этого перед выполнением компиляции:
public class Outer {
private int outerInt;
public int access$000() {
return outerInt;
}
}
class Inner$Outer {
Outer outer;
public void printOuterInt() {
System.out.println("Outer int = " + outer.access$000());
}
}
Хотя, логически, внутренний класс является частью того же самого кода, что и внешний класс, он скомпилирован как отдельный класс. Поэтому для этого требуется синтетический метод ("мост"), который должен быть создан компилятором для обеспечения доступа к приватному полю внешнего класса.
Этот JEP представляет концепцию "гнезда", где два члена одного гнезда (Outer и Inner из нашего примера) являются соседями. Два новых атрибута были добавлены в формата *.class файла: NestHost и NestMembers. Эти изменения также полезны для других, компилируемых в байткод, языков, поддерживающих вложенные классы.
Эта фича предоставляет три новых метода для java.lang.Class:
Эта функция также потребовала изменений в спецификации виртуальной машины Java (JVMS), в частности в разделе 5.4.4 «Access Control».
JEP 309: Dynamic Class-File Constants
Этот JEP описывает расширение формата *.class файла для поддержки новой формы с константным пулом CONSTANT_Dynamic (часто упоминаемой в презентациях как condy). Идея динамической константы кажется оксюмороном, но, по сути, вы можете думать о ней как о финальном (final) значении в Java. Значение константного пула не задаётся на этапе компиляции (в отличие от других констант), а используется метод начальной загрузки (bootstrap method) для определения значения во время выполнения. Поэтому значение является динамическим, но, поскольку его значение задано только один раз, оно также является константным.
Эта функция в первую очередь будет полезна тем, кто разрабатывает новые языки и компиляторы. Кто будет генерировать байт-код и *.class файлы в для запуска на JVM. Это упростит некоторые задачи.
Эта фича предоставляет новый класс java.lang.invoke.ConstantBootstraps с девятью новыми методами. Я не буду перечислять их всех здесь; это методы начальной загрузки динамически вычисляемых констант.
Эта фича требовала внесения изменений в JVMS, в частности, в том, как используется специальный байтовый код invoke и раздел 4.4 «The Constant Pool».
JEP 315: Improve Aarch64 Intrinsics
Это был JEP, внесенный Red Hat. JVM теперь может использовать больше специализированных инструкций, доступных в наборе команд Arm 64. В частности, это улучшает работу методов sin(), cos() и log() класса java.lang.Math.
JEP 318: The Epsilon Garbage Collector
Red Hat также внесла свой вклад в этот JEP. Сборщик мусора Epsilon несколько необычен, поскольку он не собирает мусор! Он будет выделять новую память, если это потребуется при создании новых объектов, но не освобождает пространство, занятое объектами без ссылок.
Казалось бы, в чем тогда смысл? Есть, как минимум, два применения:
Если пространство кучи исчерпано, последующая работа JVM может быть сконфигурирована одним из трех способов:
JEP 324: Key Agreement with Curve25519 and Curve448
Криптографические стандарты постоянно меняются и совершенствуются. В этом случае существующая схема Диффи-Хеллмана с эллиптической кривой заменяется на Curve25519 и Curve448. Это ключевая схема соглашения, определённая в RFC-7748.
Платформа Java поддерживает Unicode, для обеспечения обработки всех наборов символов. Поскольку Unicode был обновлен до версии 10, JDK также был обновлен для поддержки этой версии стандарта.
Я всегда заинтригован, чтобы увидеть, что разработчики Unicode включают в новые версии. Unicode 10 имеет 8 518 новых символов. Это включает в себя символ биткоиа, набор символов Nushu (используемый китайскими женщинами для написания стихов), а также Soyombo и Zanabazar Square (являются символами, используемыми в исторических буддийских текстах для написания санскрита, тибетского и монгольского языков). Добавилось также много других Эмоджи, включая долгожданный (по-видимому) Colbert Emoji.
Не забывайте, что начиная с JDK 9 вы можете использовать UTF-8 в файлах свойств (.properties). Это означает, что любой символ Юникода может использоваться в таких файлах. Включая Emojis. Или Nushu.
Flight Recorder — это низкоуровневая структура сбора данных для JVM. До JDK 11 это была коммерческая функция в готовом наборе Oracle JDK. Теперь, когда Oracle устраняет функциональные различия между Oracle JDK и OpenJDK, эта функция была внесена в OpenJDK.
JEP выделяет четыре основных свойства:
Для этого есть два новых модуля: jdk.jfr и jdk.management.jfr.
JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms
Подобно JEP 324, это обновление шифров, используемых JDK. Реализация шифров ChaCha20 и ChaCha20-Poly1305, как указано в RFC 7539. ChaCha20 — это относительно новый потоковый шифр, который может заменить старый, небезопасный шифр потока RC4.
JEP 331: Low-overhead Heap Profiling
Немного удивительно, что это JEP, был внесён Google. Это дает возможность получить информацию о распределении кучи объектов Java в JVM.
Основные свойства:
JEP 332: Transport Layer Security (TLS) 1.3
TLS 1.3 (RFC 8446) является "капитальным ремонтом" протокола TLS и обеспечивает значительное повышение безопасности и производительности по сравнению с предыдущими версиями. JDK теперь поддерживает это, хотя это не распространяется на Datagram Transport Layer Security (DTLS).
JEP 333: ZGC A Scalable, Low Latency Garbage Collector
Это новый экспериментальный сборщик мусора, предназначенный для использования с приложениями, для которых требуется большая (многогигабайтная) куча и низкая задержка. Он использует кучу одного поколения (что немного необычно, учитывая общепринятый шаблон использования Weak Generational Hypothesis) и выполняет большинство (но не все) работы GC одновременно с приложением. Это делается используя механизм "барьера" при чтении, который перехватывает каждое чтение объекта из приложения и гарантирует правильность вернувшейся ссылки. Это устраняет проблему одновременного перемещения объектов во время работы потоков приложений.
ZGC — region-based (как и G1), NUMA aware и compacting сборщик мусора. Не предназначен как сборщик общего назначения.
Если вы действительно хотите pauseless сборщик мусора с низкой задержкой, от всего сердца могу порекомендовать C4 в нашей Zing JVM.
JEP 335: Deprecate the Nashorn Scripting Engine
Nashorn был представлен в JDK 8 как более эффективная замена Rhino Javascript движку. Цель состоит в том, чтобы удалить Nashorn вместе с API и jjs инструментом из будущей версии Java. Когда это произойдет, пока не решено. Была предложена возможность использования Graal VM в качестве замены, но как это будет работать, пока неясно.
JEP 336: Deprecate the Pack200 Tools and APIs
Pack200 — это схема сжатия для JAR-файлов, используется ещё со времён Java SE 5.0. С введением JPMS в JDK 9 Pack200 больше не используется для сжатия самого JDK. Инструменты pack200 и unpack200 и API Pack200 в java.util.jar теперь устарели и могут быть удалены в будущей версии JDK. Когда это произойдет, не указано.
JDK 11 — следующая версия LTS JDK (так определяет Оракл за которым следуют все остальные). Несмотря на то, что функций, ориентированных на разработчиков, не так много, в JVM много что изменилось, это закладывает основу для будущих более важных функций.
Zulu сборки JDK 11 можно найти здесь и абсолютно бесплатно!
Пришло ли время начать перенос ваших приложений в JDK 11?
(прим. переводчика: просьба, все ошибки перевода и другие неточности присылать в ЛС)
К сожалению, не доступен сервер mySQL