Вышла общедоступная версия 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)
phillennium
14.09.2021 19:32+8Минутка рекламы: кто сейчас поймал себя на мысли «как уже вышла 17-я, я ещё даже про 14-ю ничего не понял, как за этим всем уследить», тем @lanyв субботу объяснит сразу всё (участие бесплатно).
solntsepek
15.09.2021 04:54-1Ничего не сказано о возрастании системных требований...
прям как в доме повешенного молчат про верёвку.
HukuToc2288
15.09.2021 10:09+1А тем временем в Android ещё только прикрутили нормальную поддержку Java 8...
ksbes
15.09.2021 11:17+1Ну там dalvik — етить его — всё тормозит. Новая java-машина не нужна. А именно по языку таких уж критических изменений нет — больше сахар. А те изменения что есть — с лихвой перекрываются Kotlin.
Гуглу бы (ну или струемозгам) делать бы прямой компилятор Kotlin в dalvik!HukuToc2288
15.09.2021 12:41+1Ну далвика сейчас нет практически, сейчас там ART, но по факту там крутится всё та же джава, хотя и скомпиленая в нативы. Отсюда и идут различные интересные вещи, когда приложение разваливается на старых телефонах, если случайно написал list.sort вместо Collections.sort(list), ибо фича Java 8, но компилятор об этом умолчит. Хотя бы с котлином сейчас всё более-менее работает
Nexus217
21.09.2021 19:05Если нужна новая версия Java, всегда можно скомпилировать OpenJDK и сунуть в приложение. Во всём кроме GUI работает отлично.
MonkeyClicker
15.09.2021 10:30-13Жабе уже пора на свалку истории. Все фичи копируются из Scala. Не проще ли уже взять Scala и сделать поддержку этого синтаксиса на нативном уровне, без промежуточных конвертаций?
igorp1024
15.09.2021 11:07+4С тем же успехом можно говорить о том, что фичи в джаву копируются из Kotlin.
MonkeyClicker
16.09.2021 00:58kotlin вышел на 7 лет позже, здесь так же понятно, кто у кого копипастил
dougrinch
16.10.2021 11:21Если предположить, что вы не просто набрасываете, а действительно так считаете, то вот рекомендую очень хорошее видео. Тут автор котлина сам рассказывает что и откуда взято и почему это нормально.
ruslan_sverchkov
15.09.2021 21:50+10Ну многие джаву любят за отсутствие «фичей», а не за наличие) так что нет, пусть живет)
chabapok
17.09.2021 14:03Сломали jaxb %(. Это по сравнению с 11.
Homyakin
21.09.2021 07:16+1А в чем именно проявляется сломанность? Использую JAXB для парсинга xml и не заметил проблем пока что.
chabapok
21.09.2021 10:44Так и бывает обычно: какие-то приложения на новой jvm продолжают стабильно работать, какие-то другие - работают нестабильно, а какие-то третьи - вообще не компиляться, а иногда даже никто не знает, почему. В моем случае был экзепшен, и ругалось на Unsafe, (насколько помню). Почему вообще могло понадобится для парсинга использовать ансейф - я без понятия. Если нужны подробности, то могу посмотреть версии либ, и что за экзепшен там был. В 11, кстати, это jaxb по сравнению с 8 было тоже переделано, и при простой смене jvm не работало.
Например, для той же idea никто никогда не спешит менять jvm на новую.
weoz
Java хотят LTSить каждые два года вместо трёх https://mreinhold.org/blog/forward-even-faster
orionll Автор
А ещё обновления будут выходить 3 года, а не полгода, как это было для Java 9-16.