Скрытые и не очень скрытые
бриллианты в Java 20

Обзор предварительных и инкубаторных JEP в Java 20, а также некоторых более мелких улучшений, багфиксов и прекращений поддержки
В марте 2023 года состоялся последний функциональный релиз платформы Java, который был своевременно выпущен в рамках шестимесячного каденса релизов Java 20 Как и Java 19, которая стала общедоступной в сентябре 2022 года, Java 20 нацелена на JEPs (в данном релизе 7) - из проектов Panama, Amber, и Looм.
Для демонстрации кода в этой статье используется инструмент Java 20 jshell. Чтобы запустить код и протестировать его при желании, загрузите JDK 20, откройте терминал, проверьте версию и запустите jshell. Обратите внимание, что вы можете увидеть более новую версию JDK, и это нормально.
Статью в оригинале можно прочитать здесь

[mtaman]:~ java -version
 openjdk 20 2023-03-21
 OpenJDK Runtime Environment (build 20+36-2344)
 OpenJDK 64-Bit Server VM (build 20+36-2344, mixed mode, sharing)

[mtaman]:~ jshell --enable-preview
|  Welcome to JShell -- Version 20
|  For an introduction type: /help intro

Jshell>
Три JEP в Java 20 опубликованы как модули-инкубаторы для получения обратной связи от
разработчиков. API модуля-инкубатора может быть изменен или полностью удален — то есть, не будет выпущен в будущих релизах JDK. Поэтому не следует использовать инкубаторные функции на продакшене. Если же все-таки хотите использовать модули-инкубаторы, проставьте JVM флаг -add-modules.
Другие JEP в Java 20 находятся в стадии превью. Эти функции полностью определены и реализованы, но предоставляются в раннем релизе для сбора фидбека. Вы должны предполагать, что функции в стадии превью могут и будут изменяться, поэтому не используйте их на продакшене. Для того, чтобы отметить такие функции, используйте переключатель --enable-preview.
Это не означает, что вы не должны использовать саму Java 20 на продакшене - вы должны! Помимо JEPs, есть и другие небольшие улучшения и исправления.
Единственное, что вы не должны использовать на продакшене, это функции, находящиеся на стадии инкубатора или превью.
В этой статье будут описаны семь JEP, а затем погрузимся в другие изменения в Java 20, включая изменения, которые не дошли до уровня уровня JEP, усовершенствования платформы, исправления и багфиксы, прекращения поддержки и удаления. Кстати, предыдущая статья из этой серии - "Наглядный обзор Java 20", - посвящена JEPs с примерами кодирования, которые здесь не дублируются. Эта статья и без них достаточно длинная!
В релиз Java 20 попали семь успешных JEP (на один больше, чем предсказывалось в
предыдущей статье). Scoped values (ограниченные значения) - это новая функция
находящаяся в стадии инкубатора, в то время как остальные шесть JEP - это повторные
представления и обновления уже известных функций в стадии инкубатора и превью,
которые появились в Java 19 или ранее.
Эти JEP часто группируются на основе их долгосрочных проектов Java, как показано в
таблице 1
JEPs в Java 20
Помимо этого, должно быть ограничено время жизни переменных на уровне потока: как только метод, который первоначально передал данные, завершает работу, любые данные, переданные через переменную для каждого потока, больше не должны быть доступны.
Виртуальные потоки (второе превью), JEP 436. Виртуальные потоки фундаментально
переопределяют взаимодействие между средой окружения Java runtime и базовой
операционной системой, устраняя при этом различные препятствия для
масштабируемости. Тем не менее, они не меняют кардинально способ создания и сопровождения параллельных программ. Виртуальные потоки ведут себя почти так же,
как и привычные потоки, и в них почти нет дополнительного API.
Первое превью виртуальных потоков было представлено в Java 19 В Java 20 JEP 436
предоставляет второе превью виртуальных потоков в более старом JEP 425, чтобы
получить дополнительные отзывы. Если же фидбека больше не будет или если не будет
внесено существенных улучшений в JEP 436, виртуальные потоки, вероятно, станут
готовой к производству функцией в предстоящем релизе Java 21.
Структурированный параллелизм (второй инкубатор), JEP 437. API структурированного
параллелизма нацелено на упрощение многопоточного программирования и помощь с управлением и отменой ошибок; API рассматривает параллельные задачи, работающие в разных потоках, как единую единицу работы, улучшая наблюдаемость и надежность.
JEP 437 по сути переиздает версию JEP 428 для Java 19, чтобы снова собрать
дополнительный фидбэк. Единственным существенным изменением является
обновленный класс StructuredTaskScope, который поддерживает наследование значений scoped (из JEP 429) потоками, созданными в области задач. Это упрощает обмен неизменяемыми данными между всеми дочерними потоками.
Проект Amber
JEP 432 и JEP 433 связаны с проектом Amber, который создан, чтобы повысить
продуктивность разработчиков.
Паттерны записей (второе превью), JEP 432. Паттерны записей были предложены в
качестве функции на первое превью в JEP 405 в Java 19 Паттерны записи обеспечивают
элегантный способ доступа к элементам записи после проверки типа. JEP 432 - это второе превью, в котором представлены дальнейшие усовершенствования. И основные
изменения следующие:
·Добавлена поддержка вывода аргументов типа для общих паттернов записей
·Добавлена поддержка паттернов записей для появления в заголовке расширенного
утверждения
·Убрана поддержка именованных паттернов записей
Сопоставление паттернов для коммутатора (четвертое превью), JEP 433
Сопоставление паттернов для переключения было предложено для превью уже несколько раз, начиная с Java 17 JEP 433 предлагает четвертое превью, чтобы обеспечить дальнейшую коэволюцию сопоставления паттернов с функцией предварительного просмотра паттернов записи в JEP 432 Ниже перечислены основные изменения с момента третьего предварительного просмотра:
· Исчерпывающий переключатель (выражение switch или оператор pattern switch)
над классом перечисления теперь выбрасывает MatchException, а не
IncompatibleClassChangeError, если во время выполнения не применяется метка
switch.
· Грамматика метки switch была упрощена.
· Вывод аргументов типа для общих паттернов записи теперь поддерживается в
выражениях и операторах switch, наряду с другими конструкциями,
поддерживающими паттерны.
Проект Panama
Эта инициатива, включающая JEP 434 и JEP 438, направлена на улучшение
совместимости между JVM и четко определенными "чужими" (не Java) API. Эти API
часто включают интерфейсы, используемые в библиотеках языка C.
API иностранных функций и памяти (второе превью), JEP 434 Этот JEP определяет
API, с помощью которого программы Java могут взаимодействовать с кодом и данными за пределами среды выполнения Java. Благодаря эффективному вызову внешних функций, которые находятся вне JVM, и безопасному доступу к внешней памяти, которой JVM не управляет, API позволяет Java-программам вызывать родные библиотеки и обрабатывать родные данные без хрупкости и опасности Java Native Interface (JNI).
Было уже несколько версий этой функции, начиная с Java 17 Первоначально было
предложено два отдельных API, но в Java 19 они были объединены в один JEP. JEP 434
включает в себя следующие дополнительные усовершенствования, основанные на
фидбэке:
·Абстракции MemorySegment и MemoryAddress унифицированы, что ведет к тому,
что сегменты памяти нулевой длины теперь моделируют адреса памяти.
·Уплотненная иерархия MemoryLayout облегчает использование с сопоставлением
паттернов в выражениях и операторах switch, предоставляемых JEP 433
·MemorySession был разделен на область действия (arena scope) и SegmentScope для
облегчения совместного использования сегментов. У арены есть ограниченное и
детерминированное время жизни - она жива с момента, когда клиент открывает
арену, до момента, когда клиент закрывает арену.
Vector API (пятый инкубатор), JEP 438
Этот API помогает выражать векторные вычисления, которые надежно компилируются во время выполнения, в оптимальные векторные инструкции на поддерживаемых архитектурах CPU. Целью является достижение гораздо более высокой производительности, чем при скалярных вычислениях.
Версии этого API разрабатывались начиная с Java 16 Эта пятая версия содержит
небольшой набор багфиксов и улучшений производительности.
Скрытые бриллианты: Самые важные изменения, не связанные с JEP
Java 20 поставляется с сотнями улучшений производительности, стабильности и
безопасности, помимо семи JEP, описанных ранее. Вот наиболее значительные изменения, не относящиеся к JEP.
Предупреждения о приведении типов в составных присваиваниях с возможными
преобразованиями с потерями. Когда я спрашиваю разработчиков о вычислениях,
включающих составные присваивания, многие из них дают неправильный ответ, потому
что не знают, что когда тип правого операнда в составном присваивании не совместим с
типом переменной, происходит приведение типа. Данные могут быть потеряны из-за
возможного преобразования с потерями.
Так, например, какая разница между следующими строками кода при их компиляции?

a = a + b;
a += b;
Многие разработчики Java скажут, что разницы нет. Однако в Java эти две операции не
всегда эквивалентны. Если a - short, а b - int, то вторая операция приведет к следующей
ошибке компилятора, поскольку a + b возвращает int, который не может быть присвоен
короткой переменной a без явного приведения:

LossyConversions.java:8: error: incompatible types: possible lossy conversion from int to short
               a = a + b;
                     ^
С другой стороны, a += b разрешено, поскольку компилятор вставляет неявное
приведение в составное присваивание. Оператор a += b эквивалентен a = (short) (a + b), где левые 16 бит результата int усекаются при приведении к short, что приводит к
потенциальной потере информации. Следующий интерфейс компилируется без каких-
либо предупреждений, однако при его выполнении вы можете получить неожиданные
результаты:

public interface LossyConversions {

       static void main(String... args) {

              short a = 30000;
              int b   = 45000;

              a += b;
              System.out.println(a);
       }
}

[mtaman]:~ javac LossyConversions.java
[mtaman]:~ java LossyConversions
    9464
Обратите внимание, что результат не равен ожидаемым 75 0 Усеченный результат
потери конверсии: 9,464.
Для решения этой проблемы в Java 20 была введена опция компилятора (javac) lint под
названием lossy-conversions, которая генерирует предупреждения о приведении типов в
составных присваиваниях (таких как += или *=), которые могут привести к потере
данных. Предупреждения, подобные следующим, оповещают разработчиков о
потенциально нежелательном поведении:

[mtaman]:~ javac -Xlint:lossy-conversions LossyConversions.java
LossyConversions.java:8: warning: [lossy-conversions] implicit cast from int to short in compound assignment is possibly lossy
              a += b;
                   ^
1 warning
Вы можете замьютить эти предупреждения с помощью @SuppressWarnings("lossy-
conversions"), но не стоит этого делать.
Новые API для именованных групп обмена ключами TLS и DTLS. Java 20 добавила
два новых API для настройки именованных групп алгоритмов обмена ключами,
используемых в соединениях Transport Layer Security (TLS) и Datagram Transport Layer
Security (DTLS).

javax.net.ssl.SSLParameters.getNamedGroups()
javax.net.ssl.SSLParameters.setNamedGroups()
Поставщик базового ключа может определить именованные группы по умолчанию для
каждого соединения. Однако именованные группы можно настроить, установив
системное свойство jdk.tls.namedGroups или используя метод setNamedGroups(). Если
системное свойство не равно и используется метод setNamedGroups(), тогда
передаваемые именованные группы будут переопределять группы по умолчанию для
указанного соединения.
Обратите внимание, что некоторые провайдеры могут не поддерживать эти новые API. В
таких случаях провайдер может игнорировать заданные именованные группы.
Новая опция командной строки JMOD. Инструмент jmod позволяет создавать архивы
JMOD - тип архивов, представленный в Java 9, который обеспечивает модульный способ
упаковки компонентов среды выполнения Java и их зависимостей. Java 20 предоставляет
опцию --compress, которая позволяет указать уровень сжатия, используемый при создании архива JMOD.
По умолчанию инструмент jmod сжимает содержимое архива JMOD, используя формат
файла ZIP. Допустимые значения для параметра --compress - от zip-0 до zip-9, где zip-0
означает отсутствие сжатия, а zip-9 обеспечивает наилучшее сжатие. По умолчанию
используется значение zip-6.
Почему бы всегда не выбирать zip-9? Если вы оптимизируете скорость сжатия или
распаковки, вам может понадобиться выбрать меньшее значение или даже нулевое.
GarbageCollectorMXBean для приостановки разметки (remark) и очистки (cleanup) в
G1
. Сборщик мусора Garbage-First (G1) теперь имеет новый GarbageCollectorMXBean под
названием G1 Concurrent GC. Этот бин сообщает продолжительность и количество пауз
при сборке мусора в процессах разметки (remark) и очистки (cleanup).
Эти паузы происходят во время полного цикла параллельной разметки, увеличивая
счетчик коллекции на два (один для паузы разметки и один для паузы очистки),
аналогично полю CGC команды jstat -gcutil. Во время этих пауз G1 Concurrent GC также
обновляет пул памяти для G1 Old Gen MemoryManagerMXBean.
Улучшенное управление параллельными потоками в G1. В Java 20 сборщик мусора
G1 имеет новый контроллер для создания одновременных потоков; он выделяет меньше
потоков и продлевает уточнение, чтобы повысить эффективность барьера записи.
В результате старые параметры командной строки, которые использовались для задания
значений параметров для старого контроллера, устаревают. При указании в командной
строке любого из следующих параметров будет выведено предупреждающее сообщение:

-XX:-G1UseAdaptiveConcRefinement
-XX:G1ConcRefinementGreenZone=buffer-count
-XX:G1ConcRefinementYellowZone=buffer-count
-XX:G1ConcRefinementRedZone=buffer-count
-XX:G1ConcRefinementThresholdStep=buffer-count
-XX:G1ConcRefinementServiceIntervalMillis=msec
Эти опции будут полностью удалены в будущем, и их использование приведет к
завершению запуска JVM.
Превентивные сборки мусора отключены по умолчанию. Сборщик мусора G1 в Java
17 ввел превентивные сборки мусора, что позволило избежать дорогостоящих сбоев
эвакуации, вызванных всплесками выделения, когда куча была почти заполнена.
Однако эти превентивные сборки приводят к дополнительной работе по сборке мусора,
поскольку устаревание объекта основано на количестве сборок мусора. Это вызывает
преждевременное продвижение в "старое поколение", что приводит к увеличению объема данных и увеличению объема работ по сборке мусора для удаления этих объектов.
В большинстве случаев эта функция приносит больше вреда, чем пользы. Поскольку сбои при эвакуации обрабатываются более эффективно, эта функция была отключена по
умолчанию в Java 20 Она все еще может быть повторно включена с помощью опций
командной строки -XX:+UnlockDiagnosticVMOptions и -XX:+G1UsePreventiveGC.
Поддержка Unicode 15.0. Java 20 была обновлена до версии 15 Unicode, которая
включает несколько обновлений, таких как база данных символов Unicode и приложения
стандарта Unicode № 9, № 15 и № 29 Более подробную информацию можно найти в релиз ноутах Консорциума Unicode (Unicode Consortium's release notes.).
Аналогично, в Java теперь класс java.text.BreakIterator ведет себя по-другому, следуя
разрывам расширенных графемных кластеров, определенных в стандарте #29
Консорциума Юникод для анализа границ символов. Это обновление может привести к
изменениям в поведении, поскольку предыдущая реализация в основном разбивала текст на границы отдельных кодовых точек для большинства символов.
Также в Java 20 локальные данные были обновлены и теперь можно использовать
репозиторий Common Locale Data Repository (CLDR) Консорциума Unicode версии 42
Ниже перечислены некоторые заслуживающие внимания изменения, которые могут
повлиять на форматирование:
- Использование символа at для стандартного формата даты и времени было
прекращено.
- Неразрывный пробел вместо обычного пробела имеет префикс a.
- Информация о первом дне недели для Китая (CN) была исправлена.
- Японский язык теперь поддерживает числа до 9 999
Данные о часовых поясах обновлены до версии 2023c IANA. Версия 2023c tz включает в себя изменения, внесенные в релизы 2022b и 2022a, которые объединили различные регионы с идентичными данными временных меток после 1970 года в единую базу данных часовых поясов. Хотя все идентификаторы часовых поясов остались прежними, объединенные часовые пояса относятся к общей базе данных часовых поясов.
Скрытые бриллианты: Усовершенствования Java 20
Оптимизированные внутренние функции для алгоритмов шифрования. Java 20
предоставляет две новые внутренние функции. Помните, что флаги, управляющие
внутренними функциями, требуют опции -XX:+UnlockDiagnosticVMOptions.

В Java 20 была добавлена поддержка оптимизированного выполнения алгоритма
аутентификации сообщений Poly1305 на платформах x86_64 с использованием
инструкций AVX512. Это улучшение применяется к коду аутентификации
сообщений Poly1305, предоставляемому провайдером SunJCE, и доступно по
умолчанию на поддерживающих платформах x86_64. Для отключения этой
оптимизации можно использовать параметр командной строки -XX:-
UsePoly1305Intrinsics.
В Java 20 добавлена поддержка интринсиков для шифра ChaCha20 на платформах
x86_64 и aarch64. Это обеспечивает оптимизированные внутренние реализации для
шифра ChaCha20, используемого провайдером SunJCE. Оптимизированные
процедуры предназначены для чипсетов x86_64 с поддержкой инструкций AVX,
AVX2 или AVX512, а также для чипов aarch64 с поддержкой набора инструкций
Advanced SIMD. Эти интринсики включены по умолчанию на поддерживаемых
платформах, что позволяет использовать оптимизированную реализацию шифра
ChaCha20. Однако, если необходимо отключить эти интринсики, можно
использовать опцию командной строки -XX:-UseChaCha20Intrinsics.
Новые события JDK Flight Recorder. Java 20 предоставляет два новых события.
· jdk.InitialSecurityProperty: это событие записывает детали начальных свойств
безопасности, загруженных через класс java.security.Security, и содержит два поля.
Первое - это ключ свойства безопасности, а второе - значение, соответствующее
значению свойства безопасности. Это новое событие включено по умолчанию.
Системное свойство java.security.debug=properties будет выводить первоначальные
свойства безопасности в стандартный поток ошибок с помощью этого нового
события и существующего события jdk.SecurityPropertyModification (которое не
включено по умолчанию). Запись JDK Flight Recorder может отслеживать
начальные установки всех свойств безопасности и любые последующие изменения.
· jdk.SecurityProviderService: это событие записывает детали вызовов метода
java.security.Provider.getService(String type, String algorithm); его поля включают тип
сервиса, имя алгоритма и поставщика безопасности. Оно не включено по умолчанию, но может быть активировано через конфиги JDK Flight Recorder или стандартные опции.
Улучшения Javadoc. Есть три улучшения Javadoc.
· Javadoc теперь может автоматически генерировать уникальные ID атрибуты для
всех HTML заголовков в комментариях документации. Эти идентификаторы могут
быть использованы в качестве якорей ссылок для более удобной навигации в
созданной документации. Это обновление особенно полезно для больших
документов с большим количеством разделов и подразделов, позволяя
пользователям быстро переходить к определенному содержанию.
· Были внесены улучшения в обобщенные теги {@link}, {@linkplain} и @see в
Javadoc для возможности ссылаться на определенные пользователем якори в
сгенерированной документации. Добавление двойного хэш-знака (##) после имени
элемента позволяет отличить эти ссылки от ссылок на члены, и таким образом
создает возможность ссылаться на конкретные разделы или заголовки в документе.
Это улучшение обеспечивает большую гибкость в навигации по документации,
позволяя создавать точечные ссылки на нужные разделы.
· Документация Javadoc теперь включает подробную информацию о JEPs, связанных
с функциями предварительного просмотра, на странице Preview API. Это позволяет
лучше понять происхождение и назначение функций, находящихся на стадии
ревью, в чем вы можете убедиться, сравнив Рисунок 1 и Рисунок 2
Новые конструкторы для класса java.security.InvalidParameterException. Два новых конструктора
-InvalidParameterException(String,
Throwable) и InvalidParameterException(Throwable). Эти конструкторы упрощают создание объектов invalidParameterException с указанием причины исключения.
В Java представлены новые методы для преобразования значений в формат с плавающей точкой половинной точности и обратно. Два новых метода, java.lang.Float.floatToFloat16 и java.lang.Float.float16ToFloat, позволяют конвертировать значения в формат IEEE 754 binary16 с половинной точностью и обратно. Однако, при оптимизации этих методов JIT-компилятором возможны некоторые особенности, связанные с возвращаемыми значениями NaN (не число).
Чтобы запретить JIT-компилятору оптимизировать эти методы, используйте следующие
параметры командной строки:

-XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_floatToFloat16,_float16ToFloat
Скрытые драгоценности: Исправления ошибок Java 20 и сопутствующие изменения
Ограничения Java XSL Template.
Предположим, вы используете XSLT-процессор JDK
для преобразования таблиц стилей в объекты Java. Если XSL-шаблон слишком большой,
вы можете столкнуться с исключением:

com.sun.org.apache.xalan.internal.xsltc.compiler.util.InternalError: Internal XSLTC error: a method in the translet exceeds the Java Virtual Machine limitation on the length of a method of 64 kilobytes. This is usually caused by templates in a stylesheet that are very large. Try restructuring your stylesheet to use smaller templates.
Для предотвращения этой проблемы вы можете разбить таблицу стилей на более мелкие
шаблоны или использовать сторонние JAR-файлы, добавленные в classpath, чтобы
переопределить XSLT-процессор JDK.
Изменения в библиотеках ядра java.net. Были внесены следующие четыре изменения:
· Входные потоки HTTP-ответов при прерывании выбрасывает исключение
IOException. Когда вы используете метод ResponseSubscribers::ofInputStream, он
возвращает экземпляр InputStream, известный как входной поток ответа HTTP. В
Java 20 реализация метода чтения по умолчанию в этих потоках была изменена.
В предыдущих версиях, если поток, выполняющий операцию чтения, был прерван,
прерывание игнорировалось. Однако, в новых версиях, если тот же поток будет
прерван во время блокировки операции чтения, то запрос будет отменен. Входной
поток будет закрыт, статус прерывания потока будет установлен в true, и будет
выброшено исключение IOException.
· Конструкторы URL, вызываемые с неправильным форматов ввода, могут
выбросить исключение MalformedURLException в тех случаях, когда оно не было
вызвано ранее. В Java 20 способ разбора входных данных в конструкторах URL
стал более строгим. Если вы вызовете эти конструкторы с неправильно
отформатированными входными данными, вы можете получить исключение
MalformedURLException, которое раньше не возникало.
Раньше некоторые проверки валидации и парсинга выполнялись при вызове
методов URL::openConnection или URLConnection::connect. Теперь эти проверки
выполняются раньше, в самих конструкторах URL. Это означает, что если
возникает проблема с форматированием URL, которая ранее могла быть
обнаружена только при открытии соединения, то теперь может быть выброшено
исключение MalformedURLException при создании самого URL.
Это изменение затрагивает только те URL, которые используют встроенные в JDK
реализации обработчиков потоков. URL, использующие сторонние реализации, не
затрагиваются. Чтобы вернуться к предыдущему поведению, установите системное
свойство -Djdk.net.url.delayParsing=true при запуске вашей программы. Обратите
внимание, что это свойство предоставлено для обратной совместимости и может
быть удалено в будущем.
· В Java 20 было изменено значение таймаута по умолчанию для неработающих
соединений, создаваемых java.net.http.HttpClient. Время ожидания HttpClient по
умолчанию теперь составляет 30 секунд. Это означает, что если соединение будет
простаивать в течение 30 секунд, оно будет закрыто HttpClient. Ранее значение
тайм-аута по умолчанию для соединений HTTP/1.1 и HTTP/2 составляло 1200
секунд.
· Java 20 вводит тайм-аут неработающего соединения для HTTP/2. Свойство
jdk.httpclient.keepalivetimeout можно использовать для установки общесистемного
значения в секундах для закрытия неработающих соединений для HTTP/1.1 и HTTP/2 при использовании HttpClient. Кроме того, вы можете использовать
свойство jdk.httpclient.keepalivetimeout.h2 для установки значения таймаута
исключительно для HTTP/2, независимо от того, установлено ли свойство
jdk.httpclient.keepalivetimeout во время выполнения.
Изменено поведение методов java.math.BigDecimal.movePointLeft()
java.math.BigDecimal.movePointRight().
Предположим, вы используете эти два метода с аргументом ноль на цели с отрицательной шкалой. В Java 20 эти методы вернут результат, численно эквивалентный цели, но с другим немасштабированным значением и
масштабом.
В предыдущих релизах эти методы возвращали результат с одним и тем же
немасштабированным значением и масштабом, что не соответствовало спецификации.
Однако это изменение относится только к случаям, когда цель имеет отрицательный
масштаб, а аргумент равен нулю; в остальных случаях поведение остается прежним.
Идентификатор объекта, используемый для методов удаления и замены
IdentityHashMap.
IdentityHashMap - это уникальный тип карты, который считает ключи
равными, только в том случае, если они идентичны. Сравнение с помощью оператора ==
возвращает true, в отличие от выполнения функции equals().
Однако, когда в интерфейс Map в Java 8 были добавлены стандартные методы
remove(Object key, Object value) и replace(K key, V oldValue, V newValue), они не были
переопределены в IdentityHashMap для использования == вместо equals(), что приводило к ошибке. Именно это и было исправлено в Java 20
Позиционная запись FileChannel не определена в режиме APPEND. Спецификация Java для java.nio.channels.FileChannel была обновлена, чтобы внести ясность в поведение метода FileChannel::write(ByteBuffer, long) при попытке записи в определенную позицию.
Когда java.nio.File.StandardOpenOption.APPEND передается в FileChannel::open при
открытии канала, поведение при записи в определенную позицию зависит от системы. В
то же время канал открывается в режиме добавления.
Это означает, что результат может отличаться в зависимости от операционной системы. В некоторых операционных системах байты будут записаны в указанную позицию. В других операционных системах указанная позиция будет проигнорирована, а байты будут
добавлены в конец файла.
Имена файлов для Unicode Normalization Format D (NFD) не нормализуются на
macOS.
Нормализация имен файлов для версии NFD от Apple была изменена на macOS.
Ранее имена файлов нормализовались для NFD на HFS+ до macOS 10.13. Однако эта
нормализация больше не выполняется на файловой системе APFS в macOS 10.13 и более поздних версиях.
Если вы хотите вернуться к предыдущему поведению и нормализовать имена файлов к
NFD, установите jdk.nio.path.useNormalizationFormD в true.
Сообщения HelloVerifyRequest, используемые для возобновления DTLS. В Java 20 было внесено исправление в реализацию SunJSSE DTLS, которое по умолчанию включает обмен cookies для всех рукопожатий, включая новые и возобновленные. Вы можете отключить эту функцию для возобновленных рукопожатий, установив системное свойство jdk.tls.enableDtlsResumeCookie в false. Это свойство влияет только на обмен файлами cookie для возобновленных рукопожатий.
Улучшенные переключатели enum.
В Java 20 есть изменение, связанное с выражениями-переключателями над типом enum. Когда вы включаете функции предварительного просмотра с помощью команды --enable-preview, и если выражение селектора выдает неожиданное значение константы enum, тогда вместо ошибки IncompatibleClassChangeError будет выброшено исключение MatchException. Такая ситуация может возникнуть только в том случае, если после компиляции переключателя в класс enum добавляется новая константа enum.
Движок JavaScript для FXML отключен по умолчанию. Начиная с Java 20, механизм сценариев JavaScript для FXML по умолчанию отключен, и при загрузке файлов .fxml, содержащих инструкцию обработки JavaScript (PI), будет выброшено исключение.
Чтобы включить эту функцию, если ваш JDK имеет механизм сценариев JavaScript,
установите системное свойство -Djavafx.allowjs=true.
Соединения JMX по умолчанию используют ObjectInputFilter. Для Java 20 агент по умолчанию в Java Management Extensions (JMX) был обновлен, чтобы ограничить типы,
которые сервер будет десериализовать с помощью ObjectInputFilter в соединении
удаленного вызова метода (RMI). Это должно работать нормально для обычного
использования MBeans в JDK, однако если приложения зарегистрировали свои MBeans в MBeanServer платформы, для них может понадобиться расширить фильтр, чтобы
включить любые дополнительные типы, которые их MBeans принимают в качестве
параметров.
Шаблон фильтра задается в файле JDK/conf/management/management.properties с помощью свойства com.sun.management.jmxremote.serial.filter.pattern. Фильтр по умолчанию охватывает любой тип, который могут использовать OpenMBeans и MXBeans.
Фильтрация сериализации и формат шаблона фильтра подробно описаны в Java Core
Libraries Developer Guide. Если необходимо передать какие-либо дополнительные типы
Java, фильтр по умолчанию можно переопределить с помощью опции -
Dcom.sun.management.jmxremote.serial.filter.pattern=.
Скрытые бриллианты: устаревания и удаления в Java 20
Методы изменены так, чтобы выбрасывать UnsupportedOperationException. Java 20 удаляет возможность приостановить или возобновить поток с помощью методов
Thread.suspend() и Thread.resume(). Эти методы были склонны к тупиковым ситуациям и
были объявлены устаревшими с JDK 1.2. Они были заменены на выбрасывание
исключения UnsupportedOperationException. Аналогично, возможность остановить поток с помощью метода Thread.stop() была удалена из-за его небезопасной природы, выбрасывающей исключение java.lang.ThreadDeath.
Конструкторы java.net.URL устарели.
Начиная с Java 20, для разбора или конструирования URL следует использовать java.net.URI. Предположим, что экземпляр java.net.URL все еще необходим для открытия соединения. В этом случае java.net.URI можeт сконструировать или разобрать строку URL, вызвав URI::create() и затем вызвав URI::toURL() для создания экземпляра URL.
Например, вот старый код.

URL url = new URL("https://blogs.oracle.com/authors/mohamed-taman");
А вот новый код.

URL url = URI.create("https://blogs.oracle.com/authors/mohamed-taman").toURL();
Когда для построения URL требуется пользовательский обработчик потока, используйте
новые методы URL::of(URI, URLStreamHandler).
Апплеты управления JMX устарели и подлежат удалению. Функция управляющего
апплета JMX (m-let) бесполезна для современных приложений и будет удалена в одном из
будущих релизов. Следующие публичные классы устаревают и больше не будут
поддерживаться: MLet, MLetContent, PrivateMLet и MLetMBean.
Это устаревание не повлияет на JMX-агент, который используется для мониторинга
встроенных инструментов JVM, или на любой инструментарий, который зависит от JMX.
Текст потока удален из Subject.current. В Java 20 произошли изменения в спецификации
Subject.current. Ранее предполагалось, что при создании нового потока, Subject будет
унаследован. Но теперь Subject хранится в AccessControlContext. AccessControlContext
наследуется при создании платформенных потоков, но не для виртуальных потоков, так
как они не захватывают контекст вызывающего в момент создания потока.
DTLS 1 и TLS-ECDH_* отключены. В Java 20 протокол DTLS 1 был отключен по
умолчанию из-за его слабой безопасности и отсутствия поддержки надежных наборов
шифров. Для этого DTLS 1 был добавлен в свойство безопасности
jdk.tls.disabledAlgorithms в конфигурационном файле java.security. Попытка использовать
DTLS 1 приведет к выбрасыванию SSLHandshakeException.
Вы можете снова включить этот протокол на свой страх и риск, удалив DTLSv1.0 из
свойства безопасности jdk.tls.disabledAlgorithms.
Аналогично, наборы шифров TLS_ECDH_* были отключены с помощью добавления
ECDH в свойство безопасности jdk.tls.disabledAlgorithms в файле конфигурации
java.security. Эти наборы шифров не обладают достаточной секретностью и практически
не используются на практике. Хотя некоторые из них уже были отключены из-за
использования отключенных алгоритмов, таких как 3DES и RC4, теперь отключены и
остальные. Любые попытки использовать наборы шифров, начинающиеся с TLS_ECDH_,
приведут к выбрасыванию SSLHandshakeException. Вы можете снова включить эти наборы шифров на свой страх и риск, удалив ECDH из свойства безопасности jdk.tls.disabledAlgorithms.
Выбрасывание ошибки, если файл java.security по умолчанию не загружается. Ранее, когда файл конфигурации безопасности по умолчанию conf/security/java.security не
загружался, JDK возвращался к использованию жестко заданной конфигурации по
умолчанию. Однако в Java 20, если файл конфигурации по умолчанию не загружается,
JDK выбрасывает InternalError вместо использования жестко заданной конфигурации по
умолчанию. Это изменение призвано сделать неправильную конфигурацию более очевидной и избежать потенциальных проблем безопасности, вызванных неожиданным поведением.

Заключение
В новом релизе Java гораздо больше различных функций, чем лишь широко
разрекламированные JEP. Изучите все эти изменения, и даже если вы не используете
функции предварительного просмотра и инкубатора, вы узнаете про различные багфиксы и другие улучшения, которые делают Java 20 достойной тестирования и использования на продакшене уже сегодня.