- PVSM.RU - https://www.pvsm.ru -
UPD. Сегодня состоится долгожданный релиз Java 14 — и пусть она не LTS, — новых фич в ней достаточно. Java 14 релизнется в течение нескольких часов — но знакомиться с ней можно уже сейчас.
В Java 14 достаточно изменений, как на уровне написания кода, так и на уровне API, GC и многих других подкапотных штук. Можно с некоторой уверенностью сказать, что если Вы знаете о каких-то суперфишках Kotlin или Python — не переживайте, с большой долей вероятности они скоро появятся в джаве. Во всяком случае, сегодняшний релиз содержит некоторые из них. Но — обо всём по порядку.
В Java 14 нас ожидают следующие нововведения:
Поговорим о каждом улучшении, на некоторых остановимся подробнее.
Рассмотрим ситуацию проверки типа объекта через instanceof. Если мы хотим явно кастомизировать объект в необходимый тип без риска поймать ClassCastException, нам необходимо сначала убедиться, что объект — нужного нам типа.
private static void showNameIfToy(Object o) {
if (o instanceof Toy) { //проверяем, действительно ли объект - класса Toy
Toy t = (Toy) o; //присваиваем ему ссылку
System.out.println("Toy's name: " + t.getName()); //что-то делаем с объектом
}
}
В данном примере, который встречается, кстати, сплошь и рядом, мы а) убеждаемся, что перед нами объект нужного типа, б) присваиваем ему ссылку, в) что-то с ним делаем, исходя из нашей логики. В Oracle, похоже, разглядели некоторую пышность кода в данной конкретной конструкции, и решили сократить её ровно на одну ступень. Теперь можно писать так:
private static void showNameIfToy(Object o) {
if (o instanceof Toy t) { //проверяем объект и сразу присваиваем ему ссылку
System.out.println("Toy's name: " + t.getName()); //сразу можем приступать к дальнейшим действиям
}
}
Насколько всё это действительно удобно и полезно, оставляю судить тебе, читатель. Да, действительно, эта конструкция в данном виде встречается, наверное, в 99% случаев, связанных с instanceof, и, возможно, упрощение приживётся (а может, и нет). Время покажет.
Мы, разработчики, ребята стреляные, нас установкой джарника не испугаешь, а как быть простому пользователю, который не хочет знать при установке JVM, что она установлена ещё на 3 миллиардах машин, не хочет знать про кросс-платформенность Java, а просто хочет ткнуть 2 раза .exe-файл, если у него винда, или просто перетащить .app-приложение в папку «Приложения», если у него мак, и не париться? Как создать все условия для программистов, чтобы они упаковывали свои приложения в привычные конечному потребителю «экзешники»?
Похоже, в Oracle осознали проблему и решили написать упаковщик, который будет сразу паковать приложения в подходящий для платформы формат:
Выглядит это примерно так:
$ jpackage --name myapp --input lib --main-jar main.jar --type exe
--name myapp — будущее имя приложения
--input lib — источник архива jar
--main-jar main.jar — имя архива, содержащего основной класс
--type exe — тип, в который будет упаковано приложение.
После упаковки, Вы сможете дважды ткнуть на myapp.exe (если у Вас Windows) и установить его как обычное windows-приложение. Графический интерфейс представлен JavaFX.
Попробуем собрать такое приложение, используя платформу Windows.
Итак, возьмём проект отсюда:
https://github.com/promoscow/bouncer [1]
При отправке GET-запроса мы получаем сообщение: «Bounce successfull» и timestamp.
Собираем джарник. Поскольку у нас gradle, он в папке build/libs.
Переходим в папку build, вводим необходимый минимум команд:
jpackage --name bouncer --input libs --main-jar bouncer.jar --type exe
Получаем, действительно, exe-шник.
Тыкаем два раза в экзешник, происходит обычная установка приложения.
Ого!
Приложение установилось и ждёт своего часа.
В Program Files в папке bouncer можно запустить bouncer.exe.
Существует такая проблема — NUMA, Non-Uniform Memory Access, Неодинаковый доступ к памяти. Иными словами, доступ к удалённым сокетам в многосекционных машинах может занимать значительное время, особенно это касается сборщика мусора G1. В Java 14 попытались решить эту проблему следующим образом:
Куча G1 организована как набор областей фиксированного размера. Регион обычно представляет собой набор физических страниц, хотя при использовании больших страниц (через -XX:+UseLargePages ) несколько регионов могут составлять одну физическую страницу.
Если добавить при инициализации JVM параметр +XX:+UseNUMA, то регионы будут равномерно распределены по общему количеству доступных узлов NUMA.
В Oracle обещают, что хоть данный подход не совсем гибкий, в дальнейшем они его улучшат.
Есть такая штука в Java, как JFR — Java Flight Recording — фиксация событий «на лету», которая позволяет мониторить около 500 разновидностей событий во время работы приложения. Проблема в том, что большинство из них можно посмотреть только в логах. Улучшение, предлагаемое Oracle, заключается в том, чтобы реализовать обработчик, например, лямбда-функцию, которая будет вызываться в ответ на событие.
Вот как это выглядит:
try (var rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
rs.onEvent("jdk.CPULoad", event -> System.out.println(event.getFloat("machineTotal")));
rs.start();
}
Это событие каждую секунду выводит в консоль загрузку процессора.
Ещё одно явное удобство, призванное упростить поиск ошибки в коде. Представим себе конструкцию — планета, на планете много стран, в каждой стране много городов.
public class Planet {
private List<Country> countries;
//...
}
public class Country {
private List<City> cities;
//...
}
public class City {
private String name;
//...
}
Мы решили вывести на экран хэшкоды всех городов, находящихся на планете:
planet.getCountries().forEach(c -> c.getCities().forEach(city -> city.hashCode()));
Но не подумали об обязательной инициализации полей. И в какой-то момент получили NullPointerException:
Exception in thread "main" java.lang.NullPointerException
at ru.xpendence.jep_358_nullpointerexception.Main.main(Main.java:19)
Какое поле у нас null? planet? country? city??? Мы не знаем. Мы ставим на нужную строчку брейкпоинт и, вздохнув, идём дебажить.
В Java 14 NullPointerException более информативен:
Exception in thread "main" java.lang.NullPointerException: Cannot assign field "cities" because "countries" is null
at Main.main(Main.java:21)
...
И сразу всё понятно. countries is null.
Не зря в Oracle сменили релизный цикл на полугодовой. Тектонические сдвиги в IT-отрасли заставляют бодрее шевелиться даже таких лидеров. И если, к примеру, Kotlin и Python при своём появлении испытывали влияние Java (про Python так, во всяком случае, сказано в Википедии), то теперь Java оглядывается на своих, так сказать, последователей. Про Python будет ниже, но следующую фичу Oracle точно посмотрели в Kotlin. Речь о data-классах, которые в Java теперь называются record.
Что такое data-класс? Напишем обычный POJO:
public class Station {
private String name;
private Coordinates coordinates;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Coordinates getCoordinates() {
return coordinates;
}
public void setCoordinates(Coordinates coordinates) {
this.coordinates = coordinates;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlainStation that = (PlainStation) o;
return Objects.equals(name, that.name) &&
Objects.equals(coordinates, that.coordinates);
}
@Override
public int hashCode() {
return Objects.hash(name, coordinates);
}
@Override
public String toString() {
return "PlainStation{" +
"name='" + name + ''' +
", coordinates=" + coordinates +
'}';
}
}
Тут у нас всё подряд — геттеры, сеттеры, equals, hashcode, toString… Чтобы как-то избавиться от этого безобразия, хорошие люди придумали Lombok.
В Jetbrains проблему решили в своё время более радикальным способом — придумав для Kotlin data-классы. Такой класс со всеми стандартными методами выглядит так:
data class Station(val name: String, val coordinates: Coordinates)
И всё. В Oracle, похоже, посмотрели на данную конструкцию и сделали точно такую же, только record:
public record RecordStation(String name, List<Coordinates> coordinates) {}
Он содержит в себе стандартные геттеры, сеттеры, equals, hashcode и toString.
Класс такого типа уже можно создать в IDEA 2020.1.
Получается, что это такой иммутабельный data-класс, не предназначенный для каких-то сложных логических действий с ним, а предназначенный для передачи данных, и всё.
Похоже, в Oracle плотно взялись за switch. До текущего релиза это была довольно громоздкая конструкция, и выглядела она так:
public static void translateDayOfWeekOld(String dayOfWeek) {
switch (dayOfWeek) {
case "MONDAY":
System.out.println("Понедельник");
break;
case "TUESDAY":
System.out.println("Вторник");
break;
case "WEDNESDAY":
System.out.println("Среда");
break;
case "THURSDAY":
System.out.println("Четверг");
break;
case "FRIDAY":
System.out.println("Пятница");
break;
case "SATURDAY":
System.out.println("Суббота");
break;
case "SUNDAY":
System.out.println("Воскресенье");
break;
default:
System.out.println("Day of week not found, try again with today day of week");
String displayName = LocalDate.now().getDayOfWeek().name();
translateDayOfWeek(displayName);
}
}
Обработка одного условия занимает минимум три строчки — case, действие, break. Теперь, с расширенной функциональностью switch, мы можем сократить вышеописанную конструкцию до такого:
public static void translateDayOfWeek(String dayOfWeek) {
switch (dayOfWeek) {
case "MONDAY" -> System.out.println("Понедельник");
case "TUESDAY" -> System.out.println("Вторник");
case "WEDNESDAY" -> System.out.println("Среда");
case "THURSDAY" -> System.out.println("Четверг");
case "FRIDAY" -> System.out.println("Пятница");
case "SATURDAY" -> System.out.println("Суббота");
case "SUNDAY" -> System.out.println("Воскресенье");
default -> {
System.out.println("Day of week not found, try again with today day of week");
String displayName = LocalDate.now().getDayOfWeek().name();
translateDayOfWeek(displayName);
}
}
}
Согласитесь, довольно компактно, модно и с лямбдами. Стоит ли это того, решать Вам.
Кстати, написанный по старым правилам switch IDEA 2020.1 заботливо предложит переписать на новый лад.
Тут всё просто. В Oracle решили, что не стоит тратить ресурсы на поддержку портов Solaris и SPARC, а освободившихся сотрудников переключить на разработку новых фич.
Об удалении сборщика мусора CMS говорили ещё два года назад, в 9-м релизе. За это время вышло два сборщика мусора — ZGC и Shenandoah. В то же время, ни один из заслуживающих доверия Oracle контрибьюторов не уделил внимания поддержке CMS.
В общем, доктор сказал в морг — значит, в морг.
Обычно мы разворачиваем приложения на платформах Linux, но для локальной разработки мы часто используем Windows и Mac. Для этих нужд в Java 14 портировали сборщик мусора ZGC на эти две платформы.
Существует крайне редко используемая конфигурация сборщика мусора — когда молодой ParallelScavenge комбинируют с SerialOld. Зачем это делают — непонятно, поскольку ParallelScavenge является параллельным, а SerialOld — наоборот, нет. Включение этой комбинации требует особых танцев с бубном и требует немалой крови разработчиков. «Oracle заботится о вас», поэтому помечает эту конфигурацию как устаревшую и надеется отправить её в скором времени к сборщику мусора CMS.
Возрадуемся, братья.
Пришёл черёд pack200, unpack200 и API pack200 отправиться на заслуженный отдых. И причина в моральном устаревании этого упаковщика. Когда-то давно, когда интернет был модемным и скорость у него была 56k (бумеры помнят), JDK приходилось выкачивать часами. Был придуман упаковщик, который лучше сжимал JDK и уменьшал время скачивания. Также, им можно было сжимать аплеты и клиентские приложения. Но время шло, и при текущих скоростях упаковщик не актуален.
Следующие пакеты будут удалены:
java.util.jar.Pack200
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker
а также, модуль jdk.pack.
В своё время, Java оказала (в числе полутора десятков других языков) влияние на Python. Поскольку Python стремительно набирает популярность и уверенно толкается с другими лидерами IT-чартов Java и C, нет ничего зазорного в том, чтобы что-то подсмотреть и у него. В своё время в Java 9 появился JShell, очень похожий на питоний Jupiter. И вот, настало время текстовых блоков.
Когда нам нужно записать строку, мы пишем строку:
String s = "Строка";
Когда нам нужно записать отформатированную строку, мы пишем что-то такое:
String oldHtml = "<html>nt<body>ntt<p>Hi all!</p>nt</body>n</html>";
Текст абсолютно нечитаемый. И вот, в Java 14 проблема решена. Теперь, используя тройные кавычки, можно писать любой текст:
String html = """
<html>
<body>
<p>Hi all!</p>
</body>
</html>
""";
Намного удобнее и читабельнее. Мы можем просто скопировать в код любой текст и не возиться с табами и переносами. Красота! Если же нам просто нужно в таком тексте перенести текст на другую строку, не делая при этом переноса, мы можем воспользоваться новым литералом — обратным слэшем. Этот символ вообщает, что следующий за ним перенос — не перенос. Пример:
String text = """
Богами вам ещё даны
Златые дни, златые ночи,
И томных дев устремлены
На вас внимательные очи.
Играйте, пойте, о друзья!
Утратьте вечер скоротечный;
И вашей радости беспечной
Сквозь слёзы улыбнуся я.
""";
Вывод:
Богами вам ещё даны Златые дни, златые ночи, И томных дев устремлены На вас внимательные очи. Играйте, пойте, о друзья! Утратьте вечер скоротечный; И вашей радости беспечной Сквозь слёзы улыбнуся я.
Бывает такое, что приложение обращается к внешней памяти наподобие Ignite, mapDB, memcached и др. Существующие API для доступа к ней вполне рабочие, но Oracle захотелось чего-то глобального. Так появились абстракции MemorySegment, MemoryAddress и MemoryLayout. Пока фича находится в инкубаторе, и все желающие могут по-прежнему довольствоваться ByteBuffer, Unsafe и JNI.
Не знаю, как тебе, читатель, а мне нравится полугодовой релизный цикл, на который перешла Oracle начиная с Java 9. Теперь Oracle не ставит задач выпускать абсолютно стабильные релизы, но искушённому джависту не составит труда быть в курсе стабильности той или иной фичи, наблюдая за их развитием и тестируя что-то из инкубатора. Язык стал более живым, изменяемым, в нём стали появляться очень смелые нововведения и заимствования из других языков. Да, кто-то не поспевает за мельканием релизов, но профессия у нас такая, что надо поспевать, поэтому, хотим мы этого или нет — встречаем Java 14.
Как обычно, прилагаю проект на гитхабе с примерами кода: [тыцнуть сюда] [2]
Автор: Вячеслав Чернышов
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/349952
Ссылки в тексте:
[1] https://github.com/promoscow/bouncer: https://github.com/promoscow/bouncer
[2] [тыцнуть сюда]: https://github.com/promoscow/java-14-examples
[3] Источник: https://habr.com/ru/post/491564/?utm_source=habrahabr&utm_medium=rss&utm_campaign=491564
Нажмите здесь для печати.