Публикация

Что нового в Java 16?

В марту 2021 года Oracle выпустила Java 16 с очень большим набором нововведений. В этом посте мы рассмотрим все эти нововведения.

Этот пост является началом серии "Что нового в Java?". Далее будут опубликованы посты с этой же серии для всех версий Java начиная с 8й.

Уже сейчас мы можем установить Java 16 по ссылке или используя SDKMan:

sdk install java 16.0.0.j9-adpt

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.

Опубликовано: 24 мая 2021 г.Просмотров: 330

Комментарии

Мы не даем слово анониму 😶
Войдите, пожалуйста.

Еще никто не комментировал эту публикацию 🤔