JavaRush /Java блог /Random /Java 14: что нового?

Java 14: что нового?

Статья из группы Random
Мировые проблемы — мировыми проблемами, а новая Java — по расписанию. То есть ровно раз в полгода. Релизная версия Java 14 вышла 17 марта, и привнесла в язык несколько интересных новшеств, ориентированных на разработчиков. Java 14: что нового? - 1Среди них — экспериментальная поддержка ключевого слова record, поддержка сопоставления c образцом в операторе "instanceof", более дружественно настроенные NullPointerExceptions, расширенная “превьюшка” текстовых блоков, обновлённый switch по умолчанию и многое другое. Напомним, все новшества в Java начинаются с предложений по расширению (JEP, Java Enhancement Proposals). Разработчики предлагают изменения, их рассматривают “официальные” родители Java, а затем принимают некоторые из этих изменений, после чего те становятся частью JDK. А теперь — обо всём по порядку.

JEP 359: Records

Записи, они же — Records доступны для JDK 14 в превью-режиме, и это — нечто совершенно новое для Java. По сути перед нами — новый тип, который был разработан в ходе проекта Valhalla. Записи похожи на перечисления и позволяют упростить код. По сути, они заменяют классы, у которых есть состояние, но нет поведения. Проще говоря, есть поля, нет методов. В случае классов нам порой приходится писать много повторяющегося не всегда нужного кода: конструкторы, методы доступа, equals(), hashCode(), toString() и т. д. Чтобы избежать этого повторяющегося кода, Java планируется использовать record. Вот классический вариант:

final class Triangle {
 	public final int x;
public final int y;
public final int z;
 
    public Triangle(int x, int y, int z) {
         this.x = x;
         this.y = y;
    this.z = z;
    }
    // equals, hashCode, toString
Переходим в Java 14 и используем record:

public record Triangle(int x, int y, int z){}
Вот и всё. Учтите, что записи пока что существуют в форме превью, поэтому чтобы опробовать их на практике нужно загрузить jdk14 и ввести команду:

javac —enable-preview —release 14 Triangle.java
Записи — это классы, пускай и с ограничениями. Они не могут расширять другие классы или объявлять поля (кроме private final которые соответствуют компонентам описания состояния). Записи неявно являются final и не могут быть абстрактными. Записи отличаются от обычных классов тем, что они не могут отделить свой API от его представления. Но потеря свободы компенсируется повышенной точностью. Компоненты записи также неявно являются final.

JEP 305: Pattern Matching для instanceof (Preview)

Фича Pattern Matching, представленная в Java 14 в превью, призвана объединить в операторе instanceof проверку типа объекта и его преобразование. Иными словами до Java 14 был бы, например, такой код:

Object object = Violin;
 
if (object instanceof Instrument) {
    Instrument instrument = (Instrument) object;
    System.out.println(instrument.getMaster());
}
Как видим, мы должны привести объект к классу, методы которого хотим использовать. Теперь Java 14 и подключённой фичей Pattern Matching позволяет сократить код до следующего:

Object object = Violin;
 
if (object instanceof Instrument instrument){
    System.out.println(instrument.getMaster());
}

JEP 343: Packaging Tool (Incubator)

В JDK 8 был инструмент javapackager, предназначенный для JavaFX. Однако после отделения JavaFX от Java вместе с выпуском JDK 11, популярный javapackager оказался более не доступным. Javapackager представлял собой инструмент упаковки. Он позволял упаковывать приложения Java таким образом, чтобы их можно было установить, как и все другие “нормальные” программы. Например, создавать exe-файлы для пользователей Windows и запускать Java-приложение по-человечески — двойным щелчком мыши. Разумеется, такого инструмента очень не хватает, поэтому в JEP 343 предложили новый инструмент jpackage, который собирает Java-приложение в пакет для конкретной платформы, содержащий все необходимые зависимости. Поддерживаемые форматы пакетов для конкретной платформы:
  • Linux: deb и rpm
  • macOS: pkg и dmg
  • Windows: MSI и EXE

JEP 345: NUMA-Aware Memory Allocation для G1

JEP 345 служит исключительно для реализации поддержки NUMA (Non-uniform memory access). Это архитектуры с неоднородным доступом к памяти, способом настройки кластера микропроцессора в многопроцессорную систему, при которой память может быть распределена локально: каждое ядро процессора получает небольшой объем локальной памяти, при этом другие ядра имеют к ней доступ. JEP 345 планирует оснастить сборщик мусора G1 возможностью рационально использовать такие архитектуры. Помимо всего прочего, такой подход помогает повысить производительность на очень мощных машинах.

JEP 349: JFR Event Streaming

Java Flight Recorder (JFR) теперь является частью OpenJDK и поэтому находится в свободном доступе. В JDK 14 добавлен API для отслеживания на лету событий JFR (JDK Flight Recorder), в частности — для организации непрерывного мониторинга активных и неактивных приложений. Записываются те же события, что и для не-потокового варианта, с накладными расходами менее 1%. Таким образом, потоковая передача событий будет осуществляться одновременно с вариантом без потоковой передачи. Однако JEP 349 не должен разрешать синхронные callback’и для соответствующего потребителя. Даже данные из записей, хранящихся в промежуточной памяти, не должны быть доступны. Технически пакет jdk.jfr.consumer в модуле jdk.jfr будет расширен функциональностью для асинхронного доступа к событиям.

JEP 352: Non-Volatile Mapped Byte Buffers

Как известно, Java NIO (New IO) File API существует с JDK 1.4,, а затем было представлено новое усовершенствование под названием Path. Path — это интерфейс, который заменяет класс java.io.File как представление файла или каталога, когда мы работаем в Java NIO. JEP 352 расширяет MappedByteBuffer для загрузки части файловых данных в энергонезависимую память (NVM). Эта память компьютера, данные в которой не будут потеряны даже в случае отключения питания (её часто называют постоянной памятью) используется для постоянного хранения данных. Это предложение по улучшению Java предоставляет новый модуль и класс для JDK API: модуль jdk.nio.mapmode, предлагает новые режимы (READ_ONLY_SYNC, WRITE_ONLY_SYNC) для создания отображаемых байтовых буферов (MappedByteBuffer), ссылающихся на NVM.

JEP 358: Helpful NullPointerExceptions

Теперь исключения NullPointerExceptions будут более дружественными к программисту. В том смысле, что описание исключения будет гораздо более информативным, чем раньше. А всё потому, что JVM научили более точно анализировать инструкции байт-кода программы, и она может указать, какая именно переменная приводит к нулевому значению. Допустим, у нас есть код:

a.getMessage().getUserInfo().getName()
В любой из последних Java мы получим обычный лог ошибки, который не отвечает на вопрос, кто именно null:

Exception in thread "main" java.lang.NullPointerException
	at Main.main(Main.java:12)
А вот что выдаст Java 14, если вы решитесь испробовать данную превью-фичу:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "UserInfo().getName()" because the return value of "Message().getUserInfo()" is null
	at Main.main(Main.java:12)
Такая цепочка гораздо более понятна, и позволяет гораздо быстрее взяться за устранение ошибки.

JEP 361: Switch Expressions (Standard)

Обновлённый оператор Switch был доступен ещё в предыдущих Java 12 и 13, но только как превью-функция, то есть, она не была включена по умолчанию. Теперь в JDK 14 всё работает “из коробки”. Java 14 представляет новую упрощенную форму блока switch с метками case L -> .... Новая форма в некоторых случаях упрощает код. Вот пара примеров. Предположим, что у нас есть enum, который описывает дни недели. Мы можем написать классический код (до Java 14):

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}
А вот — вариант с использованием Java 14:

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}
Также можно писать многострочные блоки и возвращать значение с новым ключевым словом yield:

int result = switch (s) {
    case "Working from Home" -> 1;
    case "Working from Office" -> 2;
    default    -> {
        System.out.println("Neither Home nor Office… Cafe? Car? Park?...");
        yield 0;
    }
};
Есть ещё несколько важных вещей, которые нужно иметь в виду при использовании новых switch. В частности, нужно помнить, что варианты должны быть исчерпывающими. То есть для всех возможных значений должна быть соответствующая switch-метка. Поскольку yield теперь является ключевым словом, класс с именем yield — возможен в Java 14. А вообще, если хотите научиться использовать обновлённые “свичи”, переходите на JEP 361, и изучайте. Там много интересной информации.

JEP 362: Deprecate the Solaris and SPARC Ports

Вряд ли многие наши читатели помнят об операционной системе Solaris. Эта операционка на базе UNIX, созданная родителями Java — компанией Sun Microsystems, использовалась преимущественно для серверов на SPARC-архитектуре… Слишком много незнакомых слов на квадратный сантиметр? Ничего страшного: JEP 362 прекращает поддержку платформ Solaris/SPARC, Solaris/x64 и Linux/SPARC. То есть их порты теперь Deprecated, а в будущем они, скорее всего, будут удалены из OpenJDK. Однако старые версии Java (до JDK 14) относительно портов Solaris/SPARC, Solaris/x64 и Linux/SPARC должны работать без изменений. Если вы — любитель истории и интересуетесь технологиями не такого уж далёкого прошлого — гоу в “Википедию” читать об архитектуре SPARС.

JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector

Сборщик мусора CMS (Concurrent Mark Sweep) направлен на удаление, поскольку ещё два года назад был помечен как устаревший и остался без сопровождения. Однако пользователи старых версий Java, использующие CMS GC, могут выдохнуть — цель этого JEP’а не в том, чтобы удалить сборщик из более ранних выпусков JDK. Кроме того, объявлено устаревшим применение комбинации алгоритмов сборки мусора ParallelScavenge и SerialOld (запуск с опциями "-XX:+UseParallelGC -XX:-UseParallelOldGC").

JEP 364: ZGC on macOS и JEP 365: ZGC on Windows

Есть такой интересный сборщик мусора по имени Z Garbage Collector (ZGC). Он работает в пассивном режиме, и старается максимально сократить задержки из-за сборки мусора: время остановки при использовании ZGC не превышает 10 мс. Он может работать с маленькими кучами (heap) и с гигантскими (теми, что занимают много терабайт). JEP 364 и JEP 365 — практически близнецы. JEP 364 переносит Z Garbage Collector на MacOS. Часть JEP также описывает функциональность сборщика для освобождения неиспользуемой памяти устройства, как указано в JEP 351, это происходит с Java 13. Реализация ZGC в macOS состоит из двух частей:
  • Поддержка multi-mapping memory на macOS
  • Поддержка в ZGC непрерывного резервирования памяти
JEP 365 обеспечивает поддержку ZGC уже на Windows, и тоже в экспериментальном режиме. Она заключается в следующем:
  • Поддержка multi-mapping memory
  • Поддержка маппинга памяти на основе файла подкачки в зарезервированное адресное пространство
  • Поддержка маппинга и анмаппинга произвольных частей кучи
  • Поддержка коммита и анкоммита произвольных частей кучи

JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination

Этот JEP объявляет устаревшим использование комбинации алгоритмов для сборки мусора Parallel Scavenge и Serial Old. Такую комбинацию нужно было включать вручную с помощью параметров командной строки -XX: + UseParallelGC -XX: -UseParallelOldGC. Авторы считают, что комбинация очень специфична, но при этом требует существенных усилий по обслуживанию. Так что теперь опция -XX: UseParallelOldGC устарела, и в случае использования будет появляться предупреждение.

JEP 367: Remove the Pack200 Tools and API

Pack200 — это формат архива, оптимизированный для хранения скомпилированных файлов Java-классов. Этот инструмент был помечен словом deprecated (устаревший) ещё со времён Java 11. Теперь инструменты pack200, unpack200 а также Pack200 API официально заявлены на удаление из java.util.jar package. Данная технология была введена ещё в Java 5, как средство борьбы с весьма ограниченной пропускной способностью (модемы, страшно сказать и вспомнить, 56k) и недостаточным пространством для хранения на жестких дисках. Какое-то время назад в Java 9 были представлены новые схемы сжатия. Разработчикам рекомендуется использовать jlink.

JEP 368: Text Blocks (Second Preview)

Текстовые блоки впервые появились в Java 13. Это многострочные строковые литералы, которые предотвращают необходимость в большинстве escape-последовательностей, автоматически форматируют строку, а также позволяют разработчику форматировать строку при необходимости. Теперь эта полезная функция доступна в Java 14 (вторая, предварительная версия). Главная задача текстовых блоков — улучшить работу с запутанными многострочными литералами. Это существенно упрощает чтение и написание SQL-запросов, HTML- и XML-кода, JSON. Пример HTML без текстовых блоков:

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, JavaRush Student</p>\n" +
              "    </body>\n" +
              "</html>\n";
Как представить то же самое с текстовыми блоками:

String html = """
              <html>
                  <body>
                      <p>Hello, JavaRush Student</p>
                  </body>
              </html>
              """;
Открывающий разделитель — это последовательность из трех символов двойных кавычек ("" "), за которыми следуют ноль или более пробелов, а затем — разделитель строки. Содержимое начинается с первого символа после разделителя строки открывающего разделителя. Закрывающий разделитель представляет собой последовательность из трех символов двойных кавычек. Содержимое заканчивается на последнем символе перед первой двойной кавычкой закрывающего разделителя. Содержимое может непосредственно вмещать символы двойных кавычек, в отличие от символов в строковом литерале. Использование \ в текстовом блоке разрешено, но не обязательно или не рекомендуется. Жирные разделители (" "") были выбраны таким образом, чтобы символы могли отображаться без экранирования, а также визуально отличать текстовый блок от строкового литерала. В начале 2019 года в JEP 355 предложили текстовые блоки в качестве продолжения JEP 326 (Raw String literals), однако их отозвали. Позднее в том же году, в JDK 13 представили функцию предварительного просмотра текстового блока, и теперь в Java 14 в неё добавили две новые escape-последовательности. Это символ новой строки (line-terminator), обозначенный \, и вторая — для пробела (single space), обозначается /s. Пример использования символов новой строки без текстовых блоков:

String literal = "This is major Tom to Ground Control " +
"I am stepping through the door... " +
"Wait… What???";
А теперь — с эскейп-последовательностью \<line-terminator>:

String text = """
                This is major Tom to Ground Control \
                I am stepping through the door... \
                Wait… What???\
                """;
Эскейп-последовательность \s используется для учета завершающих пробелов, которые по умолчанию игнорируются компилятором. Он сохраняет все имеющиеся перед ним пробелы. Пример:

String text1 = """
               line1
               line2 \s
               line3
               """;
  
String text2 = "line1\nline2 \nline3\n";
text1 и text2 — идентичны.

JEP 370: Foreign-Memory Access API (Incubator)

Многие популярные библиотеки и программы Java имеют доступ к внешней памяти. Например, Ignite, MapDB, Memcached и Netty ByteBuf API. При этом они могут избежать затрат и непредсказуемости, связанных со сборкой мусора (особенно при обслуживании больших кэшей), совместно использовать память между несколькими процессами, а также сериализовать и десериализовать содержимое памяти путем сопоставления файлов в памяти (например, с помощью mmap). Однако Java API до сих пор не обзавелась подходящим решением для доступа ко внешней памяти. В JDK 14 можно подключить предварительный вариант API Foreign-Memory Access, который позволяет приложениям Java безопасно и эффективно получить доступ к областям памяти, вне кучи виртуальной машины Java, с помощью новых абстракций MemorySegment, MemoryAddress и MemoryLayout.

Выводы

Ну как вам? По сравнению с Java 13, новая Java 14 предлагает гораздо больше важных улучшений в самых разных областях. Скорее всего, наиболее важными для разработчиков окажутся обновлённые switch, расширенные исключения NullPointerExceptions и records. Или нет?.. Не забудьте опробовать новые фичи Java 14, это очень полезно даже для новичков. Удачи в обучении!
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
wan-derer.ru Уровень 40
17 июня 2020
Это всё, конечно, интересно, но что там с десктопами? В частности - JavaFX. Будет развиваться? Что там с совместимостью версий? Или будет/есть что-то другое что следует изучать? Да так чтобы было кроссплатформенно, да без перекомпиляции :)
Андрей Уровень 19
20 марта 2020
Вот здесь неплохо объясняются изменения в новой версии : https://www.youtube.com/watch?v=qN8x-aIrhxk
Aleksey JavaWin Уровень 15
20 марта 2020
А мне record понравился)
kef Уровень 39
20 марта 2020
Вторые закрывающие кавычки лишние: String html = """ <html> <body>

Hello, JavaRush Student

</body> </html> """; """;
Айдар Уровень 28
19 марта 2020
как раз на днях решал задачу 3608 и ловил NullPointerException
KRonst Уровень 41
19 марта 2020
Java все больше становится похожа на Kotlin