Вышла общедоступная версия Java 17. В этот релиз попало более 2700 закрытых задач и 14 JEP'ов. Изменения API можно посмотреть по этой ссылке.


Ссылки на скачивание:



Перечислим JEP'ы, которые попали в Java 17.


Язык


Восстановление всегда строгой семантики чисел с плавающей точкой (JEP 306)


Теперь все выражения с плавающей точкой вычисляются строго на всех платформах, и разницы между строгой семантикой и семантикой по умолчанию больше нет (как это было до Java 1.2). Таким образом, ключевое слово strictfp больше не имеет эффекта, и при его использовании выдаётся предупреждение:


> javac Main.java
Main.java:1: warning: [strictfp] as of release 17, all floating-point
expressions are evaluated strictly and 'strictfp' is not required
strictfp public class Main {
                ^

sealed классы (JEP 409)


«Запечатанные» классы после двух preview итераций в Java 15 и Java 16 наконец-то стали стабильной синтаксической конструкцией и больше не требуют флага --enable-preview. Различий по сравнению с Java 16 нет.


Напомним, ключевым слово sealed можно пометить класс или интерфейс, если нужно ограничить список его наследников. Список можно указать явно, перечислив все наследники с помощью ключевого слова permits, либо он выведется неявно компилятором из всех задекларированных наследников в текущем исходном файле.



Паттерн-матчинг для switch (Preview) (JEP 406)


Появилась новая синтаксическая конструкция в режиме preview, существенно расширяющая возможности оператора switch: теперь оператор switch поддерживает не только простые тесты на равенство с константами примитивов, перечислений и строк, но и тесты по паттернам.


Простейшие паттерны это паттерны по типу:


Object o = ...
return switch (o) {
    case Integer i -> String.format("int %d", i);
    case Long l -> String.format("long %d", l);
    case Double d -> String.format("double %f", d);
    case String s -> String.format("String %s", s);
    default -> o.toString();
};

Паттерны могут снабжаться условиями путём добавления охранных паттернов:


Shape s = ...
switch (s) {
    case Triangle t && (t.calculateArea() > 100) ->
        System.out.println("Large Triangle");
    default ->
        System.out.println("A shape (possibly a small triangle)");
}

Также добавлена поддержка матчинга null с помощью отдельной метки null:


Object o = ...
switch (o) {
    case null -> System.out.println("Null");
    case String s -> System.out.println("String: " + s);
    default -> System.out.println("Other");
}

Ветки null и default можно объединять друг с другом:


String s = ...
switch (s) {
    case "Foo", "Bar" -> System.out.println("Foo or Bar");
    case null, default -> System.out.println("Null or other");
}

Все switch с паттернами должны быть исчерпывающими (даже если это switch statement, а не switch expression):


Object o = ...
switch (o) {
    case String s -> System.out.println("String: " + s);
    case Integer i -> System.out.println("Integer");
}

> javac --enable-preview --release 17 Main.java
Main.java:4: error: the switch statement does not cover all possible input values
        switch (o) {
        ^

Чтобы пример выше стал компилироваться, нужно либо добавить ветку default, либо тотальный паттерн (в данном случае case Object obj).


Старый switch statement можно продолжать делать неисчерпывающим, однако это может измениться в будущем.


Новый switch очень хорошо взаимодействует с sealed классами, поскольку круг подтипов является ограниченным, а это значит, что ветка default не является необходимой, если перечислены все возможные случаи:


sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {}

S s = ...
switch (s) {
    case A a -> System.out.println("A");
    case B b -> System.out.println("B");
    case C c -> System.out.println("C");
}

На этом беглое рассмотрение паттерн-матчинга для switch мы закончим, а про другие возможности и нюансы можно почитать на странице JEP.



API


Enhanced Pseudo-Random Number Generators (JEP 356)


В Java 17 появился новый пакет java.util.random с новыми интерфейсами и реализациями генераторов псевдослучайных чисел. Наверху иерархии новый интерфейс RandomGenerator, который является родителем как новых генераторов и специализаций (SplittableRandomGenerator, JumpableRandomGenerator, LeapableRandomGenerator, ArbitrarilyJumpableRandomGenerator), так и старых классов (Random, ThreadLocalRandom, SplittableRandom).


Таким образом, алгоритмы генерации чисел теперь стали более взаимозаменяемыми:


RandomGenerator random1 = new Random();
RandomGenerator random2 = ThreadLocalRandom.current();
RandomGenerator random3 = RandomGenerator.getDefault();
RandomGenerator random4 = RandomGenerator.of("Xoshiro256PlusPlus");

Кроме того, список генераторов ограничен не только теми, что предоставлены самим JDK, но является расширяемыем: предоставлять свои или сторонние реализации можно через механизм ServiceLoader.



Deprecate the Applet API for Removal (JEP 398)


API аплетов, которое стало deprecated в Java 9, теперь стало помечено как подлежащее окончательному удалению. Таким образом, существующий код, который использует классы из Applet API (java.applet.Applet, javax.swing.JApplet и т.д.), может перестать работать на одной из следующих версий Java (но всё ещё продолжит работать на Java 17).



Удаление RMI Activation (JEP 407)


Механизм RMI Activation, который стал deprecated в Java 15, теперь удалён окончательно. RMI Activation был частью RMI, которая использовалась в чрезвычайно малом количестве приложений и фактически устарела. Для того чтобы избежать дорогой поддержки никому не нужного механизма, было принято решение его полностью удалить.



Deprecate the Security Manager for Removal (JEP 411)


Security Manager стал помечен как подлежащий удалению. В одной из будущих версий его планируется удалить окончательно. Аннотацию @Deprecated(forRemoval=true) получили 10 классов и 10 методов из стандартной библиотеки Java. Среди них java.lang.SecurityManager, java.security.Policy, java.lang.System::setSecurityManager, java.lang.System::getSecurityManager, java.lang.Thread::checkAccess и другие. Кроме того, при вызове метода System.setSecurityManager() во время исполнения будет выдаваться предупреждение:


WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by com.foo.bar.Server
  (file:/tmp/foobarserver/thing.jar)
WARNING: Please consider reporting this to the maintainers of com.foo.bar.Server
WARNING: System::setSecurityManager will be removed in a future release

Также предупреждение будет выдаваться при установке Security Manager через аргументы командной строки указанием свойства java.security.manager:


> java -Djava.security.manager=com.foo.bar.Server MyApp
WARNING: A command line option has enabled the Security Manager
WARNING: The Security Manager is deprecated and will be removed in a future release


Начать отказ от Security Manager было решено по причине того, что он слабо отвечает современным требованиям безопасности и всё меньше и меньше пользуется спросом при разработке новых Java-приложений, но цена его поддержки остаётся очень высокой. У него хрупкая модель разрешений, сложная программная модель, и он приносит большие накладные расходы по производительности.



Foreign Function & Memory API (Incubator) (JEP 412)


Foreign Memory API, который побывал в Java 14, 15, 16 в инкубационном статусе, и Foreign Linker API, появившийся в Java 16, теперь объединены в один Foreign Function & Memory API, и он всё ещё остаётся в инкубационном статусе в модуле jdk.incubator.foreign.



Vector API (Second Incubator) (JEP 414)


Векторное API, которое появилось в Java 16 в инкубационном статусе, всё ещё остаётся в этом статусе в модуле jdk.incubator.vector.



Context-Specific Deserialization Filters (JEP 415)


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


Фабрику фильтров можно установить через метод ObjectInputFilter.Config::setSerialFilterFactory, либо через аргументы командной строки, передав имя класса фабрики в свойство jdk.serialFilterFactory.



JVM


New macOS Rendering Pipeline (JEP 382)


Реализован новый конвейер рендеринга на основе современного Apple Metal API, который был создан с целью заменить устаревший Apple OpenGL API. Новый конвейер сосуществует со старым, который пока что остаётся включённым по умолчанию, но если OpenGL не работает, то запускается новый. Таким образом, Java 17 полностью готова к запуску на будущих версиях macOS, где OpenGL API будет окончательно удалён. Также новый конвейер можно включить явно, указав опцию -Dsun.java2d.metal=true.



Порт под macOS/AArch64 (JEP 391)


OpenJDK портирован на архитектуру macOS/AArch64, и теперь список дистрибутивов OpenJDK и Oracle JDK пополнился сборками для Apple M1.



Строгая инкапсуляция внутренностей JDK (JEP 403)


Инкапсуляция внутренних API, которая была введена в Java 9 и была выключена по умолчанию в Java 16, но всё ещё могла быть включена одной опцией командной строки, теперь не может быть включена одной опцией:


> java --illegal-access=permit Main
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=permit;
  support was removed in 17.0

Таким образом, доступиться до внутренностей JDK (sun.*, com.sun.*, jdk.internal.* и т.д.) теперь можно только путём явного перечисления всех необходимых пакетов с помощью опций --add-opens или --add-exports.


Изменения не касаются критического API в модуле jdk.unsupported: классы в пакетах sun.misc и sun.reflect остаются доступными без флагов.


В будущем опция --illegal-access будет удалена окончательно, и при её использовании будет ошибка старта.



Удаление экспериментальных компиляторов AOT и JIT (JEP 410)


Удалены экспериментальные ahead-of-time и just-in-time Graal компиляторы. Такое решение было принято из-за сложности поддержки и их малой используемости (те, кому они нужны, используют GraalVM). По факту этих компиляторов уже не было в сборках JDK 16 от Oracle.


Удалению подлежали три модуля: jdk.aot, jdk.internal.vm.compiler и jdk.internal.vm.compiler.management. Модуль JVMCI (jdk.internal.vm.ci) остался, чтобы у разработчиков всё ещё оставалась возможность использовать внешний JIT-компилятор.



Заключение


Предыдущая версия OpenJDK с Long Term Support, Java 11, вышла ровно 3 года назад, хотя кажется, что она вышла совсем недавно, практически вчера. Но вот уже вышло 5 версий (12, 13, 14, 15, 16) и мы не успели опомниться, как подоспела следующая LTS-версия, Java 17.


Для тех, кто будет переходить с 11, Java 17 будет достаточно большим апгрейдом. Если сложить все релизы с 12 по 17, то в сумме появилось 5 больших языковых конструкций: выражения switch, блоки текста, записи, паттерн-матчинг для instanceof и sealed классы. Появилось очень много интересного API, сделано много улучшений производительности, оптимизировано потребление памяти, уменьшено время старта JVM. Очень много чего было удалено, поэтому могут возникнуть некоторые трудности при миграции.


Следующими большими шагами для Java будет завершение реализации проектов Valhalla, Loom и Panama, а также дальнейшие языковые улучшения в проекте Amber. Увидим ли мы всё это в следующей LTS-версии? Узнаем через 2 года.

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


  1. weoz
    14.09.2021 19:06
    +3

    Java хотят LTSить каждые два года вместо трёх https://mreinhold.org/blog/forward-even-faster


    1. orionll Автор
      15.09.2021 06:08

      А ещё обновления будут выходить 3 года, а не полгода, как это было для Java 9-16.


  1. phillennium
    14.09.2021 19:32
    +8

    Минутка рекламы: кто сейчас поймал себя на мысли «как уже вышла 17-я, я ещё даже про 14-ю ничего не понял, как за этим всем уследить», тем @lanyв субботу объяснит сразу всё (участие бесплатно).


  1. urvanov
    14.09.2021 20:06
    -1

    А что, про strictfp теперь правда так?


  1. solntsepek
    15.09.2021 04:54
    -1

    Ничего не сказано о возрастании системных требований...

    прям как в доме повешенного молчат про верёвку.


  1. HukuToc2288
    15.09.2021 10:09
    +1

    А тем временем в Android ещё только прикрутили нормальную поддержку Java 8...


    1. ksbes
      15.09.2021 11:17
      +1

      Ну там dalvik — етить его — всё тормозит. Новая java-машина не нужна. А именно по языку таких уж критических изменений нет — больше сахар. А те изменения что есть — с лихвой перекрываются Kotlin.
      Гуглу бы (ну или струемозгам) делать бы прямой компилятор Kotlin в dalvik!


      1. HukuToc2288
        15.09.2021 12:41
        +1

        Ну далвика сейчас нет практически, сейчас там ART, но по факту там крутится всё та же джава, хотя и скомпиленая в нативы. Отсюда и идут различные интересные вещи, когда приложение разваливается на старых телефонах, если случайно написал list.sort вместо Collections.sort(list), ибо фича Java 8, но компилятор об этом умолчит. Хотя бы с котлином сейчас всё более-менее работает


    1. Nexus217
      21.09.2021 19:05

      Если нужна новая версия Java, всегда можно скомпилировать OpenJDK и сунуть в приложение. Во всём кроме GUI работает отлично.


  1. MonkeyClicker
    15.09.2021 10:30
    -13

    Жабе уже пора на свалку истории. Все фичи копируются из Scala. Не проще ли уже взять Scala и сделать поддержку этого синтаксиса на нативном уровне, без промежуточных конвертаций?


    1. igorp1024
      15.09.2021 11:07
      +4

      С тем же успехом можно говорить о том, что фичи в джаву копируются из Kotlin.


      1. MonkeyClicker
        16.09.2021 00:58

        kotlin вышел на 7 лет позже, здесь так же понятно, кто у кого копипастил


        1. dougrinch
          16.10.2021 11:21

          Если предположить, что вы не просто набрасываете, а действительно так считаете, то вот рекомендую очень хорошее видео. Тут автор котлина сам рассказывает что и откуда взято и почему это нормально.

          https://youtu.be/xH-RZ9YlxH0


    1. ruslan_sverchkov
      15.09.2021 21:50
      +10

      Ну многие джаву любят за отсутствие «фичей», а не за наличие) так что нет, пусть живет)


  1. MonkeyClicker
    15.09.2021 21:19
    -1

    del


  1. chabapok
    17.09.2021 14:03

    Сломали jaxb %(. Это по сравнению с 11.


    1. Homyakin
      21.09.2021 07:16
      +1

      А в чем именно проявляется сломанность? Использую JAXB для парсинга xml и не заметил проблем пока что.


      1. chabapok
        21.09.2021 10:44

        Так и бывает обычно: какие-то приложения на новой jvm продолжают стабильно работать, какие-то другие - работают нестабильно, а какие-то третьи - вообще не компиляться, а иногда даже никто не знает, почему. В моем случае был экзепшен, и ругалось на Unsafe, (насколько помню). Почему вообще могло понадобится для парсинга использовать ансейф - я без понятия. Если нужны подробности, то могу посмотреть версии либ, и что за экзепшен там был. В 11, кстати, это jaxb по сравнению с 8 было тоже переделано, и при простой смене jvm не работало.

        Например, для той же idea никто никогда не спешит менять jvm на новую.