Что нового в Java 16?
В марту 2021 года Oracle выпустила Java 16 с очень большим набором нововведений. В этом посте мы рассмотрим все эти нововведения.
Этот пост является началом серии "Что нового в Java?". Далее будут опубликованы посты с этой же серии для всех версий Java начиная с 8й.
Уже сейчас мы можем установить Java 16 по ссылке или используя SDKMan:
sdk install java 16.0.0.j9-adpt
Содержимое
JEP 396: Strongly Encapsulate JDK Internals by Default (Futureproofing)
JEP 395: Records (New Language Features)
JEP 394: Pattern Matching for instanceof (New Language Features)
JEP 390: Warnings for Value-Based Classes (Futureproofing)
JEP 392: Packaging Tool (New Tools and Libraries)
JEP 380 Unix-Domain Socket Channels (New Tools and Libraries)
JEP 387: Elastic Metaspace (JVM Improvements)
JEP 376: ZGC: Concurrent Thread-Stack Processing (JVM Improvements)
JEP 388: Windows/AArch64 Port (Improving Productivity)
JEP 386: Alpine Linux Port (Improving Productivity)
JEP 357: Migrate from Mercurial to Git (Improving Productivity)
JEP 369: Migrate to GitHub (Improving Productivity)
JEP 347: Enable C++14 Language Features (Improving Productivity)
JEP 397: Sealed Classes (Second Preview)
JEP 393: Foreign-Memory Access API (Third Incubator)
JEP 389: Foreign Linker API (Incubator)
JEP 338: Vector API (Incubator)
JEP 395: Records
Record ранее уже был анонсирован как preview в 14 и 15 версиях Java.
И спустя 2 версии Java 16 ввела Record как основное синтаксическое нововведение, которое позволит решить проблему шаблонного кода и сделать его более читабельным.
Если вы знакомы с Kotlin например то скорее всего знакомы с ключевым словом data
класс, которое позволяет создавать дата классы без Getters и Setters и остальных формальных вещей.
Эту же проблему решала довольно популярная библиотека в мире Java Project Lombok и вот спустя долгое время мы получаем решение этой проблемы в виде Record классов.
Давайте посмотрим как выглядят POJO до появления Record классов:
class Person {
private String firstName;
private String lastName;
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int hashCode() { ... }
public String toString() { ... }
}
Такие классы обычно используются чтобы отобразить модель данных используемой в бизнес-логике.
С появлением Record классов мы можем перейти к более изысканному описанию POJO:
record Person(String firstName, String lastName) {}
Для Record классы по умолчанию будут созданы нами привычные Getters и Setters но за одним исключением. Если ранее мы бы получали firstName с Person как getFirstName()
, то для record классов это просто firstName()
.
Также будет создан toString()
и equals()
c hashCode()
которые гарантируют что два объекта равны если их типы и значение равны.
JEP 394: Pattern Matching for instanceof
Как и с record фитчей Pattern Matching также был ранее показан как preview в 14 и 15 версиях Java и финализирован в Java 16.
Вспомним тот случай, когда нам нужно проверить какого типа наш Object:
if (obj instanceof Person) {
Person person = (Person) obj;
...
}
Практически всегда делая проверку соответствия по типу далее мы преобразуем к этому типу наш объект. Так почему бы не совместить это действие что и было сделано в JEP 394:
if (obj instanceof Person person) {
...
}
В случае если условие выполнится, тогда мы можем обращаться к нашему person.
JEP 396: Strongly Encapsulate JDK Internals by Default
С выходом Java 9 был представлен новый параметр --illegal-access
позволяющий контролировать доступ к внутренним API и пакетам JDK. Такие пакеты как sun.misc.Unsafe
больше не будут открыты по умолчанию.
Теперь с приходом Java 16 параметр --illegal-access
был изменен с permit на deny. И также помечен как deprecated и в следующий версиях Java скорее всего будет и уделан.
JEP 390: Warnings for Value-Based Classes
Оберточные классы примитивов такие как Integer, Double, Character и т.д. теперь относятся к группе value-based классов. Также конструкторы этих классов были помечены как deprecated for removal.
Value-Based - классы являются неизменяемыми и создаются через фабрики.
JEP 392: Packaging Tool
Jpackage был представлен в инкубаторе Java 14 и теперь с приходом Java 16 есть полноценной ее частью и перемещена в jdk.jpackage модуль.
Он позволяет создавать нативные пакеты под разные платформы, такие как deb/rpm в Linux, pkg/dmg в macOS и msi/exe в Windows.
JEP 380: Unix-Domain Socket Channels
Появилась поддержка сокетов домена UNIX (AF_UNIX) через java.net.UnixDomainSocketAddress
. Такой вид сокетов используются для межпроцессного взаимодействия внутри одного хоста, и в них не используются сетевые соединения, что делает такое взаимодействие более безопасным и эффективным.
Также с недавних пор Unix-domain сокет поддерживается и на Windows 10.
JEP 387: Elastic Metaspace
Был улучшен Metaspace для более эффективного освобождения неиспользуемой памяти обратно операционной системе.
Metaspace - служит для хранения метаданных классов и может динамически расширять объем памяти.
JEP 376: ZGC: Concurrent Thread-Stack Processing
Обработка стеков потоков в сборщике мусора ZGC, которая была предоставлена в Java 15 теперь перенесена из safepoints в конкурентную фазу, что позволило значительно уменьшить паузы сборщика мусора.
safepoint (безопасная точка) - точка во время выполнения программы, в которой известны все корни сборки мусора и согласовано все содержимое объекта кучи.
С глобальной точки зрения, все потоки должны блокироваться в безопасной точке, прежде чем сборщик мусора сможет начать работать.
JEP 388: Windows/AArch64 Port
JDK была портирована под Windows/AArch64 архитектуру, что позволит запускать Java на компьютерах с Windows под управлением процессоров ARM64.
JEP 386: Alpine Linux Port
Прежде чем понять это нововведение вам нужно понимать что такое musl:
musl - реализация libc (стандартная библиотека языка С), предназначенная для операционных систем на базе Linux.
JEP 286 предоставил портированный JDK для дистрибутивов Linux с использованием musl в качестве стандартной библиотеки, такой как Alpine Linux, как на x64, так и на AArch64.
JEP 357: Migrate from Mercurial to Git
Исходный код проекта OpenJDK до этого момента поддерживался системой контроля версий Mercurial, которая в современном мире очень устарел и не позволяет решать большинство задач которые решает Git. С Java 16 весь исходный код OpenJDK переехал на систему контроля версий Git.
JEP 369: Migrate to GitHub
Вместе с JEP 357 все репозитории проекта OpenJDK переехали на GitHub https://github.com/openjdk.
JEP 347: Enable C++ 14 Language Features
Функциональные возможности C++ 14 будут включены в сборках JDK. Это означает, что код будет скомпилирован с параметром std=c++14
для всех платформ (Windows, Linux, macOS, AIX).
JEP 397: Sealed Classes (Second Preview)
Это вторая предварительная версия Sealed классов, представленных в JEP 360 в Java 15, с некоторыми улучшениями о которых пойдет речь ниже.
Как было указано в JEP 360 с релизом JDK 15 Sealed классы и интерфейсы ограничивают то, какие классы или интерфейсы могут их расширять или реализовывать.
Таким образом мы можем избежать непреднамеренного расширения класса к примеру.
Теперь о новом что дал нам JEP 397.
На замен restricted keyword и restricted identifier появилось новое понятие contextual keyword:
- sealed
- non-sealed
- permits
Благодаря этому компилятор может производить более строгие проверки в момент конвертации типов в иерархии которых присутствуют sealed классы.
Также, метод Class.permittedSubclasses()
был переименован в Class.getPermittedSubclasses()
.
JEP 393: Foreign-Memory Access API (Third Incubator)
Это нововведение находится в инкубаторном состоянии и пока не рекомендуется использовать в продакшене так как API еще может поменяться либо вовсе не выйти в релиз.
Это обновление включает в себя упрощенный путь выполнения наиболее распространенных и простых задач с помощью статических классов MemoryAccess без использования VarHandle.
MemoryAccess - это интерфейс предоставляющий методы записи и чтения во внешний источник памяти.
JEP 389: Foreign Linker API (Incubator)
Foreign Linker API также находится в инкубаторном состоянии и предоставляет возможность получать доступ к машинному коду используя Java заменив JNI.
Почему эта фича очень важная и значимая?
Давайте представим что у нас есть следующий код на C++:
#include <stdio.h>
void sayhello() {
printf("Hello world!");
}
Для тех кто не знаком с С++ нужно пояснить что такое Shared Library в С++
Shared libraries это файлы с расширением .so на Linux, .dll на Windows и .dylib на OS X. Весь код относящийся к "shared library" может быть использован другими программами.
Чтобы собрать код выше как "shared library" необходимо выполнить следующие команды:
gcc -c -fpic sayhello.c
gcc -shared -o sayhello.so sayhello.o
И используя нововведения Foreign Linker API мы можем выполнять этот код:
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.LibraryLookup;
import jdk.incubator.foreign.FunctionDescriptor;
import java.nio.file.Path;
import java.lang.invoke.MethodType;
public class JEP389ForeignLinkerAPI {
public static void main(String[] args) throws Throwable {
var lib = LibraryLookup.ofPath(Path.of("/path_to/sayhello.so"));
var shm = lib.lookup("sayhello").get();
var fd = FunctionDescriptor.ofVoid();
var mt = MethodType.methodType(Void.TYPE);
var mh = CLinker.getInstance().downcallHandle(shm.address(), mt, fd);
mh.invokeExact();
}
}
В примере выше мы подключаем shared library и получаем метод, который позже вызываем.
Почему это круто? Таким образом мы вызываем нативный код, который значительно быстрей выполняется и также следуя опыту Python теперь в Java мы сможем начать более эффективно использовать готовые библиотеки написанные на Cи.
JEP 338: Vector API (Incubator)
И последняя фича со списка JDK 16 это Vector API, которая также находится в инкубаторном состоянии.
jdk.incubator.vector - предоставляет API для выполнения векторных вычислений (SIMD).
SIMD - принцип компьютерных вычислений, позволяющий обеспечить параллелизм на уровне данных.
Важно понимать что эта функция не будет обеспечивать поддержку автоматической векторизации (т.е. вам нужно явно использовать API), и она будет поддерживаться только на архитектурах x64 и AArch64.