🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье вы найдёте 11 трюков для Java: от записей (Records) и паттернов до дженериков, лямбда-выражений, jpackage и jshell.
Трюк №1: Компактные конструкторы записей (Records)
Обычно в record-классах используется конструктор, который принимает все компоненты в качестве аргументов. В таком конструкторе можно проверять входные данные перед их сохранением в полях.
Но есть способ сделать код короче. Компактный конструктор позволяет опустить список параметров — они автоматически передаются. Внутри конструктора можно проверять и изменять эти параметры, но нельзя напрямую присваивать значения полям, так как это делает компилятор.
public record Range(int start, int end) {
public Range {
if (end <= start) {
throw new IllegalArgumentException();
}
}
}
Run CodeТрюк №2: Сериализация записей (Records)
В Java можно сериализовать любой объект — иногда с применением магии. Но с Records всё проще!
Наличие конструктора и геттеров позволяет сериализовать их без лишних ухищрений. Это значит, что создать надёжный сериализуемый Record — проще простого.
Трюк №3: jpackage — не только для модулей
jpackage — это инструмент командной строки, который может взять Java-приложение и превратить его в полностью автономный пакет, включающий всё: код, зависимости и даже Java-рантайм. Для формирования этого рантайма jpackage использует jlink, который можно настроить прямо в jpackage, либо передать ему уже созданный runtime-образ.
Кроме того, jpackage позволяет задать метаданные приложения (иконку, лицензию), параметры установки, конфигурацию лаунчеров, а также передать опции для JVM и самого приложения.
На выходе jpackage генерирует установочные пакеты в нативных форматах — deb/rpm для Linux, exe/msi для Windows.
И самое интересное: jpackage работает не только с модульными приложениями, но и с обычными!
# generate an application image for a modular application:
jpackage --type app-image -n name -p modulePath -m moduleName/className
# for a nonmodular application:
jpackage --type app-image -i inputDir -n name --main-class className --main-jar myJar.jar
Трюк №4: Кросс-платформенные runtime-образы
С помощью jlink можно создавать runtime-образы для разных операционных систем.
Допустим, билд-сервер работает на Linux, но нужен runtime-образ для Windows. Нужно просто скачать Windows JDK той же версии, добавить его jmods
в module-path
jlink-а на Linux — и готово.
# download JDK for Windows and unpack into jdk-win
# create the image with the jlink binary from the system's JDK
# (in this example, Linux)
$ jlink
--module-path jdk-win/jmods:mods
--add-modules com.example.main
--output app-image
Трюк №5: Метки для break и continue
Известно, что break
позволяет выйти из внутреннего цикла. Но знали ли вы, что можно присвоить break
метку, чтобы выйти сразу из внешнего цикла?
Тот же трюк работает с continue
: без метки он перескакивает остаток текущей итерации внутреннего цикла, но с меткой — делает это для внешнего.
Однако то, что этот трюк работает, не значит, что его стоит применять повсюду. Используйте это осторожно, чтобы код не превратился в лабиринт.
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() -
substring.length();
test:
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
Run CodeТрюк №6: Используем переменную из pattern matching сразу
Можно сразу использовать переменную, которую объявляет pattern matching, в том же булевом выражении.
Например, object instanceof String s
не просто проверяет тип, но и создаёт переменную s
, которую можно тут же использовать, например, для проверки && !s.isEmpty()
.
Работает в if
-выражениях с Java 16 и в switch
(пока в режиме preview) с Java 17.
Object object = // ...
if (object instanceof String s && !s.isEmpty())
System.out.println("Non-empty string");
else
System.out.println("No string or empty.");
Run CodeТрюк №7: Wildcard’ы в дженериках и подтипы
Вы знаете, как работают дженерики, и то, что List<Integer>
— это не List<Number>
. (См. рис. 1)

Но знали ли вы, что, используя wildcard’ы, можно построить иерархию типов? (См. рис. 2)
Например, List<? extends Integer>
действительно является подтипом List<? extends Number>
. И наоборот: List<? super Number>
является суперклассом List<? super Integer>
.

Трюк №8: Создание и объединение предикатов
Вам известно, как создавать предикаты с помощью лямбд, но интерфейс предоставляет кучу других методов для их создания и комбинирования. Например, для работы с булевыми выражениями можно использовать методы and
, or
и negate
.
Статический метод not
поможет инвертировать ссылку на метод. А если передать объект в isEqual
, он создаст предикат для проверки равенства экземпляров с этим объектом.
Predicate<String> isEqualToDuke = Predicate.isEqual("Duke");
Predicate<Collection<String>> isEmpty = Collection::isEmpty;
Predicate<Collection<String>> isNotEmpty = Predicate.not(isEmpty);
Run CodeТрюк №9: Создание и комбинирование компараторов
Если вам понравился предыдущий трюк, компараторы — это нечто ещё более крутое, и есть ещё больше методов для их создания и комбинирования.
Для сравнения значений типа long
, double
, float
и других используйте ссылку на метод их статического метода compare
.
Comparator<Integer> comparator = Integer::compare;
Если тебе нужно сравнить объекты по одному из их атрибутов, передайте функцию, которая его извлекает, в Comparator.comparing
. Чтобы сначала отсортировать по одному атрибуту, а потом по другому, создайте два компаратора и комбинируйте их с помощью thenComparing
.
Comparator<User> byFirstName = Comparator.comparing(User::getFirstName);
Comparator<User> byLastName = Comparator.comparing(User::getLastName);
Comparator<User> byName = byFirstName.thenComparing(byLastName);
Run CodeЕсли нужен компаратор, который использует метод compareTo
объекта, реализующего Comparable
, воспользуйтесь статическим методом naturalOrder
. Если нужно инвертировать порядок, просто вызовите reversed
.
А что насчёт null
? Не переживайте: передайте компаратор в методы nullsFirst
или nullsLast
, чтобы создать компаратор, и он будет правильно обрабатывать null
.
Comparator<Integer> natural = Comparator.naturalOrder();
Comparator<Integer> naturalNullsLast = Comparator.nullsLast(natural);
Run CodeТрюк №10: Запуск исходных файлов как скриптов
Вам известно, что с помощью команды java
можно запустить исходный файл без предварительной компиляции. Но знаете ли вы, что это можно использовать для написания полноценного скрипта на Java всего в три простых шага?
Первое: добавьте строку shebang в исходный файл, указав путь к исполняемому файлу java
, а затем --source
и версию Java, для которой написан код.
#!/path/to/your/bin/java --source 16
public class HelloJava {
public static void main(String[] args) {
System.out.println("Hello " + args[0]);
}
}
Run CodeВторое: переименуйте файл, чтобы он не заканчивался на .java
. Так будет удобнее для командной строки.
Третье: сделайте файл исполнимым через chmod +x
. Вот и всё: скрипты на Java готовы!
Трюк №11: Запуск jshell с импортами
Вы знаете, как запустить jshell
для быстрых экспериментов, но не обязательно импортировать всё вручную. Просто запустите jshell
с опцией JAVASE
, и все пакеты Java SE будут автоматически импортированы, так что можно будет сразу начинать работу.
jshell JAVASE
Перевод статьи «11 great Java tricks from dev.java».