Java 9 приносит множество новых улучшений, которые в значительной степени повлияют на ваш стиль программирования и привычки. Самое большое изменение — это модульность Java. Это еще одно большое изменение после лямбд в Java 8. В этой статье я перечисляю изменения, которые станут частью выпуска JDK 9.
- Модульная система платформы Java
- Private методы в Interface
- Клиент HTTP/2
- Инструмент JShell – REPL
- Ведение журнала платформы и JVM
- Обновления Process API
- Обновления Collection API
- Обновления Stream API
- Multi-Release JAR файлы
- Обновления тэга @Deprecated
- Класс StackWalker
- Обновления Java Docs
- Разные Другие Функции
Модульная система платформы Java
JPMS (Java Platform Module System) — это основная изюминка новой версии Java 9. Он также известен как проект Jigshaw. Модуль — это новая конструкция, как будто у нас уже есть пакеты. Приложение, разработанное с использованием нового модульного программирования, можно рассматривать как набор взаимодействующих модулей с четко определенными границами и зависимостями между этими модулями.
JPMS состоит из предоставления поддержки для написания модульных приложений, а также модуляции исходного кода JDK. JDK 9 поставляется примерно с 92 модулями (изменения возможны в версии GA). Модульная система Java 9 имеет модуль “java.base”. Он известен как Базовый модуль. Это независимый модуль и не зависит ни от каких других модулей. По умолчанию все остальные модули зависят от “java.base”.
В модульном программировании java-
- Модуль обычно представляет собой просто jar-файл, содержащий module-info.class файл в корне.
- Чтобы использовать модуль, включите файл jar в modulepath вместо пути к классу. Модульный файл jar, добавленный в classpath, является обычным файлом jar и module-info.class файл будет проигнорирован.
Типичный module-info.java класс выглядят следующим образом:
module helloworld {
exports com.howtodoinjava.demo;
}
module test {
requires helloworld;
}
Private методы в Interface
Java 8 позволяла писать методы по умолчанию в интерфейсах, и это была широко оцененная функция. Итак, после этого интерфейсам не хватает лишь нескольких вещей, и private методы были одним из них. Начиная с Java 9, вам разрешено включать private методы в интерфейсы.
Эти private методы улучшат повторное использование кода внутри интерфейсов. Например, если двум методам по умолчанию необходимо совместно использовать код, private метод интерфейса позволит им это сделать, но без предоставления этого private метода реализующим его классам.
Использование private методов в интерфейсах имеет четыре правила :
- Private метод интерфейса не может быть абстрактным.
- Private метод может быть использован только внутри интерфейса.
- Private static метод может использоваться внутри других статических и нестатических методов интерфейса.
- Private нестатические методы не могут использоваться внутри private static методов.
Пример использования частных методов в интерфейсах:
public interface CustomCalculator
{
default int addEvenNumbers(int... nums) {
return add(n -> n % 2 == 0, nums);
}
default int addOddNumbers(int... nums) {
return add(n -> n % 2 != 0, nums);
}
private int add(IntPredicate predicate, int... nums) {
return IntStream.of(nums)
.filter(predicate)
.sum();
}
}
Клиент HTTP/2
Клиент HTTP/1.1 был выпущен в 1997 году. С тех пор многое изменилось. Итак, для Java 9 был представлен новый API, который является более чистым и понятным в использовании, а также добавляет поддержку HTTP / 2. Новый API использует 3 основных класса: HttpClient, HttpRequest и HttpResponse.
Cделать запрос просто — создать клиент, создать запрос и отправить его, как показано ниже.
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(new URI("//howtodoinjava.com/")).GET().build();
HttpResponse<String> httpResponse = httpClient
.send(httpRequest, HttpResponse.BodyHandler.asString());
System.out.println( httpResponse.body() );
Приведенный выше код выглядит намного чище и читабельнее.
Новый API также поддерживает асинхронные HTTP-запросы с использованием метода HttpClient.SendAsync(). Он возвращает объект CompletableFuture, который можно использовать для определения того, был ли запрос завершен или нет. Он также предоставляет вам доступ к Http-ответу после завершения запроса. Самое приятное то, что при желании вы даже можете отменить запрос до его завершения. например
if(httpResponse.isDone()) {
System.out.println(httpResponse.get().statusCode());
System.out.println(httpResponse.get().body());
} else {
httpResponse.cancel(true);
}
Инструмент JShell – REPL
Jshell — это новый интерактивный инструмент командной строки, поставляемый с дистрибутивом JDK 9 [JEP 222] для оценки объявлений, операторов и выражений, написанных на Java. JShell позволяет нам выполнять фрагменты кода Java и получать немедленные результаты без необходимости создавать решение или проект.
Jshell очень похож на то, что у нас есть командное окно в ОС Linux. Разница в том, что JShell специфичен для Java. Он обладает множеством других возможностей, помимо выполнения простых фрагментов кода. например
- Запустить встроенный редактор кода в отдельном окне
- Запустить редактор кода по вашему выбору в отдельном окне
- Выполнить код при выполнении операции сохранения в этих внешних редакторах
- Загрузка предварительно записанных классов из файловой системы
Ведение журнала платформы и JVM
JDK 9 улучшил ведение журнала в классах платформы (классы JDK) и компонентах JVM с помощью нового API входа в систему. Это позволяет вам указать структуру ведения журнала по вашему выбору (например, Log4J2) в качестве серверной части ведения журнала для ведения журнала сообщений из классов JDK.
Есть несколько вещей, которые вы должны знать об этом API:
- API предназначен для использования классами в JDK, а не классами приложений.
- Для кода вашего приложения вы продолжите использовать другие API ведения журнала, как и раньше.
- API не позволяет настраивать регистратор программно.
API состоит из следующего:
- Служебный интерфейс,
java.lang.System.LoggerFinder
, который представляет собой абстрактный статический класс - Интерфейс, java.lang.System.Logger, который предоставляет API ведения журнала
- Перегруженный метод getLogger() в java.lang.System класс, который возвращает экземпляр регистратора.
JDK 9 также добавил новую опцию командной строки -Xlog, которая предоставляет вам единую точку доступа ко всем сообщениям, зарегистрированным из всех классов JVM. Ниже приведен синтаксис для использования параметра -Xlog:
-Xlog[:][:[][:[][:]]]
Все опции являются необязательными. Если предыдущая часть в -Xlog отсутствует, вы должны использовать двоеточие для этой части. Например, -Xlog::stderr указывает, что все части установлены по умолчанию, вывод которых задан как stderr.
Обновления Process API
До Java 5 единственным способом создания нового процесса было использование метода Runtime.getRuntime().exec(). Затем в Java 5 был представлен ProcessBuilder API, который поддерживал более чистый способ создания новых процессов. Теперь Java 9 добавляет новый способ получения информации о текущем и любом порожденном процессе.
Чтобы получить информацию о любом процессе, теперь вы должны использовать java.lang.ProcessHandle.Info интерфейс. Этот интерфейс может быть полезен для получения большого количества информации, например
- команда используемая для запуска процесса
- аргументы команды
- момент времени когда процесс был запущен
- общее время, затраченное им и пользователем, который его создал
ProcessHandle processHandle = ProcessHandle.current();
ProcessHandle.Info processInfo = processHandle.info();
System.out.println( processHandle.getPid() );
System.out.println( processInfo.arguments().isPresent() );
System.out.println( pprocessInfo.command().isPresent() );
System.out.println( processInfo.command().get().contains("java") );
System.out.println( processInfo.startInstant().isPresent() );
Чтобы получить информацию о новом порожденном процессе, используйте process.toHandle(), чтобы получить экземпляр ProcessHandle. В остальном все так, как описано выше.
String javaPrompt = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(
javaPrompt, "-version"
);
Process process = processBuilder.inheritIO().start();
ProcessHandle processHandle = process.toHandle();
Также используйте ProcessHandle.allProcesses(), чтобы получить поток ProcessHandle всех процессов, доступных в системе.
Чтобы получить список всех дочерних процессов (а также n-уровневых), используйте методы children() и descendants().
Stream<ProcessHandle> children = ProcessHandle.current().children();
Stream<ProcessHandle> descendants =
ProcessHandle.current().descendants();
Обновления Collection API
Начиная с Java 9, вы можете создавать immutable collections, такие как immutable list, immutable set и immutable map, используя новые фабричные методы. например
import java.util.List;
public class ImmutableCollections
{
public static void main(String[] args)
{
List<String> namesList = List.of("Lokesh", "Amit", "John");
Set<String> namesSet = Set.of("Lokesh", "Amit", "John");
Map<String, String> namesMap = Map.ofEntries(
Map.entry("1", "Lokesh"),
Map.entry("2", "Amit"),
Map.entry("3", "Brian"));
}
}
Обновления Stream API
Java 9 представила два новых метода для взаимодействия с Streams, такие как takeWhile / dropWhile. Кроме того, добавлены два перегруженных метода ofNullable и iterate.
Новые методы takeWhile и dropWhile позволяют получать части потока на основе предиката.
- В упорядоченном потоке takeWhile возвращает “самый длинный префикс” элементов, взятых из потока, которые соответствуют заданному предикату, начиная с начала потока. dropWhile возвращает остальные элементы, которые не были сопоставлены takeWhile.
- В неупорядоченном потоке takeWhile возвращает подмножество элементов потока, которые соответствуют заданному предикату (но не все), начиная с начала потока. dropWhile возвращает оставшиеся элементы потока после удаления подмножества элементов, соответствующих заданному предикату.
Аналогично, до Java 8 вы не можете иметь нулевое значение в потоке. Это вызвало бы исключение NullPointerException. Начиная с Java 9, метод Stream.ofNullable() позволяет создавать одноэлементный поток, который обертывает значение, если оно не равно null, или является пустым потоком в противном случае. Технически Stream.ofNullable() очень похож на проверку состояния null, когда речь идет в контексте stream API.
Multi-Release JAR файлы
Это улучшение связано с тем, как вы упаковываете классы приложений в файлы jar. Раньше вам приходилось упаковывать все классы в jar-файл и указывать путь к классу другого приложения, которое хотело бы его использовать.
Используя функцию нескольких выпусков, теперь jar может содержать разные версии класса, совместимые с разными выпусками JDK. Информация о различных версиях класса и о том, в какой версии JDK какой класс должен быть выбран загруженным классом, хранится в файле MANIFEST.MF. В этом случае файл MANIFEST.MF содержит запись Multi-Release: true в своем основном разделе.
Кроме того, META-INF содержит подкаталог versions, в подкаталогах с целочисленными именами, начинающихся с 9 (для Java 9), хранятся файлы классов и ресурсов, зависящие от версии. например
JAR content root
A.class
B.class
C.class
D.class
META-INF
MANIFEST.MF
versions
9
A.class
B.class
Давайте предположим, что в JDK 10, A.class обновятся, чтобы использовать некоторые функции Java 10, тогда этот Jar-файл может быть обновлен следующим образом:
JAR content root
A.class
B.class
C.class
D.class
META-INF
MANIFEST.MF
versions
9
A.class
B.class
10
A.class
Это выглядит действительно многообещающим шагом для решения ада зависимостей, часто наблюдаемого в больших приложениях, где jar’ы с разными версиями несовместимы друг с другом. Эта функция может оказать большую помощь в решении этих сценариев.
Обновления тэга @Deprecated
Начиная с Java 9, аннотация @Deprecated будет иметь два атрибута, forRemoval
и since
.
- forRemoval – указывает, подлежит ли аннотированный элемент удалению в будущей версии.
- since – Возвращает версию, в которой аннотированный элемент устарел.
Настоятельно рекомендуется, чтобы причина устаревания элемента программы была объяснена в документации с использованием тега @deprecated javadoc. Документация также должна предлагать и ссылаться на рекомендуемый заменяющий API, если это применимо. Заменяющий API часто имеет слегка отличающуюся семантику, поэтому такие вопросы также следует обсудить.
Класс StackWalker
Стек представляет собой структуру данных «Последний вход-Первый выход» (LIFO). На уровне JVM в стеке хранятся фреймы. При каждом вызове метода создается новый фрейм, который перемещается в верхнюю часть стека. Фрейм уничтожается (выталкивается из стека), когда вызов метода завершается. Каждый кадр в стеке содержит свой собственный массив локальных переменных, а также свой собственный стек операндов, возвращаемое значение и ссылку на пул констант времени выполнения текущего класса метода.
В данном потоке в любой момент активен только один кадр. Активный кадр известен как текущий кадр, а его метод известен как текущий метод .
До Java 8 StackTraceElement представляет собой фрейм стека. Чтобы получить полный стек, вы должны были использовать Thread.getStackTrace() и Throwable.getStackTrace(). Он возвращает массив StackTraceElement, который вы можете проитерировать, чтобы получить необходимую информацию.
В Java 9 был введен новый класс StackWalker. Класс обеспечивает простое и эффективное перемещение по стеку, используя последовательный поток кадров стека для текущего потока. Класс StackWalker очень эффективен, потому что он лениво оценивает фреймы стека.
// Prints the details of all stack frames of the current thread
StackWalker.getInstance().forEach(System.out::println);
Обновления Java Docs
Java 9 расширяет возможности инструмента javadoc для создания разметки HTML5. В настоящее время он генерирует страницы в HTML 4.01.
Чтобы сгенерировать Javadoc HTML5, параметр -html5 должен быть указан в аргументах командной строки. Чтобы сгенерировать документ в командной строке, вы должны запустить:
javadoc [options] [packagenames] [sourcefiles] [@files]
Использование HTML5 дает преимущества более простой структуры HTML5. Он также реализует стандарт доступности WAI-ARIA. Это направлено на то, чтобы облегчить людям с физическими или зрительными нарушениями доступ к страницам javadocs с помощью таких инструментов, как программы чтения с экрана.
JEP 225 предоставляет возможность поиска в javadoc элементов программы и помеченных слов и фраз.
Следующее будет проиндексировано и доступно для поиска:
- Объявленные имена модулей
- Пакеты
- Типы и элементы
- Простое имя типов параметров метода
Это реализовано на стороне клиента, с новым search.js Файл Javascript вместе с индексами, генерируемыми при создании javadoc. Окно поиска доступно на сгенерированных страницах HTML5 API.
Пожалуйста, обратите внимание, что опция поиска будет добавлена по умолчанию, но может быть отключена с помощью аргумента: -noindex.
Разные Другие Функции
В Java 9 есть и другие функции, которые я перечисляю здесь для краткого ознакомления.
- Reactive Streams API
- GC (Garbage Collector) Improvements
- Filter Incoming Serialization Data
- Deprecate the Applet API
- Indify String Concatenation
- Enhanced Method Handles
- Compact Strings
- Parser API for Nashorn