Напоминалка по устройству Java records!
На картинке показано, из каких частей состоит record и что генерирует компилятор.
Например, запись public record Person(String name, int age) создаёт final-поля, канонический конструктор, методы name(), age(), equals() и hashCode() без ручного шаблонного кода.
Сохрани, чтобы не потерять!
👉 Java Ready | #совет
На картинке показано, из каких частей состоит record и что генерирует компилятор.
Например, запись public record Person(String name, int age) создаёт final-поля, канонический конструктор, методы name(), age(), equals() и hashCode() без ручного шаблонного кода.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤6🔥6
Запускаем внешнюю команду через ProcessBuilder!
Иногда Java-приложению нужно вызвать системную утилиту, например git, ffmpeg или собственный скрипт деплоя. Склеивать такую команду в одну строку опасно, особенно если параметры приходят извне.
ProcessBuilder принимает аргументы отдельно. Так пробелы и специальные символы не превращаются в случайный shell-код.
redirectErrorStream(true) объединяет обычный вывод и ошибки. Это не даёт процессу зависнуть, когда буфер stderr заполняется, пока приложение читает только stdout.
После чтения обязательно дождитесь завершения и проверьте код возврата. Нулевой код обычно означает успех.
Для долгих команд добавьте waitFor с таймаутом и уничтожайте процесс при превышении лимита. Так интеграция с внешним инструментом не сможет навсегда заблокировать поток приложения.
👉 Java Ready | #практика
Иногда Java-приложению нужно вызвать системную утилиту, например git, ffmpeg или собственный скрипт деплоя. Склеивать такую команду в одну строку опасно, особенно если параметры приходят извне.
ProcessBuilder принимает аргументы отдельно. Так пробелы и специальные символы не превращаются в случайный shell-код.
var process = new ProcessBuilder(
"git", "status", "--short")
.redirectErrorStream(true)
.start();
redirectErrorStream(true) объединяет обычный вывод и ошибки. Это не даёт процессу зависнуть, когда буфер stderr заполняется, пока приложение читает только stdout.
try (var reader = process.inputReader()) {
var lines = reader.lines().toList();
lines.forEach(System.out::println);
}После чтения обязательно дождитесь завершения и проверьте код возврата. Нулевой код обычно означает успех.
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IllegalStateException("Command failed");
}
Для долгих команд добавьте waitFor с таймаутом и уничтожайте процесс при превышении лимита. Так интеграция с внешним инструментом не сможет навсегда заблокировать поток приложения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Ищем одинаковые файлы на Java!
В папке могут лежать копии одного файла под разными именами. Сделаем небольшую утилиту, которая сравнивает содержимое, а не названия.
В этой задаче:
Результат поможет разобрать архивы, папки и копии.
👉 Java Ready | #задача
В папке могут лежать копии одного файла под разными именами. Сделаем небольшую утилиту, которая сравнивает содержимое, а не названия.
В этой задаче:
• обходим вложенные папки
• считаем SHA-256 потоково
• группируем пути по хешу
• показываем кандидатов на дубликаты
Результат поможет разобрать архивы, папки и копии.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🔥4
Почему trim() может не убрать пробелы по краям строки?
В Java часто очищают пользовательский ввод так:
Но trim() ориентируется на символы с кодом не выше U+0020. Некоторые пробельные символы Unicode под это правило не попадают.
Например, строка может начинаться и заканчиваться широким пробелом U+2003:
Видимые на экране отступы останутся. Это может сломать сравнение логина, поиск ключа или проверку поля формы.
Начиная с Java 11 есть strip(), который опирается на определение пробельного символа в Character.isWhitespace:
Если нужна очистка только с одной стороны, есть отдельные методы:
Для проверки строки, состоящей только из пробелов, пригодится isBlank():
strip() не удаляет все символы, которые выглядят как пробелы. Для специальных форматов ввода полезно отдельно определить допустимые символы.
👉 Java Ready | #совет
В Java часто очищают пользовательский ввод так:
String name = input.trim();
Но trim() ориентируется на символы с кодом не выше U+0020. Некоторые пробельные символы Unicode под это правило не попадают.
Например, строка может начинаться и заканчиваться широким пробелом U+2003:
String input = "\u2003admin\u2003";
System.out.println(input.trim().length());
Видимые на экране отступы останутся. Это может сломать сравнение логина, поиск ключа или проверку поля формы.
Начиная с Java 11 есть strip(), который опирается на определение пробельного символа в Character.isWhitespace:
String name = input.strip();
System.out.println(name.equals("admin"));
Если нужна очистка только с одной стороны, есть отдельные методы:
String left = input.stripLeading();
String right = input.stripTrailing();
Для проверки строки, состоящей только из пробелов, пригодится isBlank():
if (input.isBlank()) {
throw new IllegalArgumentException("Empty name");
}strip() не удаляет все символы, которые выглядят как пробелы. Для специальных форматов ввода полезно отдельно определить допустимые символы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥4
Собираем Tower Defense с JavaFX и FXGL!
Автор решил попробовать FXGL на проекте и начал с игры, в которой враги идут по маршруту, а игрок расставляет башни. Получился не только экран с графикой, но и небольшой игровой цикл с ресурсами, волнами и улучшениями.
В статье показывают:
• как разложены игровые сущности и компоненты
• что происходит при размещении башни на поле
• как игра проверяет стоимость и доступность покупки
👉 Java Ready | #статья
Автор решил попробовать FXGL на проекте и начал с игры, в которой враги идут по маршруту, а игрок расставляет башни. Получился не только экран с графикой, но и небольшой игровой цикл с ресурсами, волнами и улучшениями.
В статье показывают:
• как разложены игровые сущности и компоненты
• что происходит при размещении башни на поле
• как игра проверяет стоимость и доступность покупки
Продолжай читать на Habr
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥2
Почему kubectl apply ещё не означает, что приложение заработало?
Во время деплоя часто запускают одну команду и считают её успешное завершение концом работы:
Но apply сообщает, что Kubernetes принял описание ресурса. Новые Pod могут ещё создаваться, скачивать образ, ждать readiness probe или падать при запуске.
После apply проверьте именно завершение обновления Deployment:
Команда ждёт завершения rollout и возвращает ошибку, если не дождалась его за указанное время. Название app здесь должно совпадать с именем вашего Deployment.
В скрипте команды удобно поставить рядом:
Теперь пайплайн не продолжит работу после неуспешного ожидания. Само по себе это всё ещё не заменяет проверку приложения снаружи кластера. Сервис, ingress и внешние зависимости могут ломаться отдельно.
Если обновление застряло, сначала посмотрите состояние ресурсов:
Затем откройте события Deployment и причину проблемы у конкретного Pod:
Проблемой может оказаться неверный образ, недоступный секрет, ошибка readiness probe или нехватка ресурсов.
Предыдущую ревизию можно вернуть отдельно:
После отката тоже проверьте rollout status.
➡️ DevOps Ready | #совет
Во время деплоя часто запускают одну команду и считают её успешное завершение концом работы:
kubectl apply -f deployment.yaml
Но apply сообщает, что Kubernetes принял описание ресурса. Новые Pod могут ещё создаваться, скачивать образ, ждать readiness probe или падать при запуске.
После apply проверьте именно завершение обновления Deployment:
kubectl rollout status deployment/app --timeout=120s
Команда ждёт завершения rollout и возвращает ошибку, если не дождалась его за указанное время. Название app здесь должно совпадать с именем вашего Deployment.
В скрипте команды удобно поставить рядом:
set -e
kubectl apply -f deployment.yaml
kubectl rollout status deployment/app --timeout=120s
Теперь пайплайн не продолжит работу после неуспешного ожидания. Само по себе это всё ещё не заменяет проверку приложения снаружи кластера. Сервис, ingress и внешние зависимости могут ломаться отдельно.
Если обновление застряло, сначала посмотрите состояние ресурсов:
kubectl get deployment app
kubectl get pods -l app=app
Затем откройте события Deployment и причину проблемы у конкретного Pod:
kubectl describe deployment app
kubectl describe pod POD_NAME
Проблемой может оказаться неверный образ, недоступный секрет, ошибка readiness probe или нехватка ресурсов.
Предыдущую ревизию можно вернуть отдельно:
kubectl rollout undo deployment/app
После отката тоже проверьте rollout status.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍2🔥2
Шпаргалка по sealed-иерархиям в Java!
На картинке показано, как sealed-класс ограничивает прямых наследников. Одни ветки закрываются через final, другая остаётся контролируемой через sealed, а non-sealed снова разрешает свободное наследование.
Например, для фиксированного набора фигур можно явно перечислить допустимые реализации Shape. Это помогает выразить границы модели прямо в типах и облегчает проверку вариантов при работе с pattern matching.
Сохрани, чтобы не потерять!
👉 Java Ready | #ресурс
На картинке показано, как sealed-класс ограничивает прямых наследников. Одни ветки закрываются через final, другая остаётся контролируемой через sealed, а non-sealed снова разрешает свободное наследование.
Например, для фиксированного набора фигур можно явно перечислить допустимые реализации Shape. Это помогает выразить границы модели прямо в типах и облегчает проверку вариантов при работе с pattern matching.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍2🔥2
Следим за появлением файлов в папке через WatchService!
Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.
Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
События могут повторяться, а при перегрузке наблюдатель сообщит OVERFLOW. Поэтому важную обработку лучше делать идемпотентной и предусмотреть повторное сканирование каталога после переполнения.
👉 Java Ready | #практика
Создадим наблюдатель и зарегистрируем каталог. Папка должна существовать заранее:
Path dir = Path.of("incoming");
try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);Константы ENTRY_CREATE и ENTRY_MODIFY берутся из StandardWatchEventKinds. try-with-resources закроет наблюдатель при выходе из блока.
Дальше ждём сигнал. Метод take блокирует поток, пока событие не появится:
while (true) {
WatchKey key = watcher.take();
for (WatchEvent<?> event : key.pollEvents()) {Контекст события содержит имя относительно наблюдаемой папки, а не готовый абсолютный путь. Не подставляй его отдельно от dir:
if (event.kind() == OVERFLOW) continue;
Path file = dir.resolve((Path) event.context());
System.out.println(event.kind() + " " + file);
}
После обработки обязательно сбросим ключ. Если каталог удалён или больше недоступен, reset вернёт false:
if (!key.reset()) break;
}
}
События могут повторяться, а при перегрузке наблюдатель сообщит OVERFLOW. Поэтому важную обработку лучше делать идемпотентной и предусмотреть повторное сканирование каталога после переполнения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍4🔥2
Находим три самых популярных товара!
Есть поток покупок с повторяющимися названиями. Построим небольшую сводку, которая считает частоты и показывает лидеров.
В этой задаче:
Когда данных станет очень много, вместо полной сортировки можно оставить только три записи в маленькой куче.
👉 Java Ready | #задача
Есть поток покупок с повторяющимися названиями. Построим небольшую сводку, которая считает частоты и показывает лидеров.
В этой задаче:
• собираем счётчики через Map.merge
• сортируем товары по частоте
• оставляем топ-3 и печатаем результат
Когда данных станет очень много, вместо полной сортировки можно оставить только три записи в маленькой куче.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥2👍1
Как оплачивать зарубежные сервисы в 2026 году?
Можно бегать между посредниками и бояться блокировок после оплаты, а можно выпустить международную карту Lumio Pay и пользоваться любимыми сервисами без рисков.
— выпуск карты за 2 минуты
— лучший курс пополнения на рынке (у конкурентов на 20% выше)
— пополнение рублями или криптой
— чистые BIN карт, оплата без риска блокировок
Пока все ищут идеальное решение, оно у тебя перед глазами: @LumioPay
Можно бегать между посредниками и бояться блокировок после оплаты, а можно выпустить международную карту Lumio Pay и пользоваться любимыми сервисами без рисков.
— выпуск карты за 2 минуты
— лучший курс пополнения на рынке (у конкурентов на 20% выше)
— пополнение рублями или криптой
— чистые BIN карт, оплата без риска блокировок
Пока все ищут идеальное решение, оно у тебя перед глазами: @LumioPay
❤1
Почему после Stream.toList() нельзя добавить элемент?
При работе со Stream часто нужно собрать результаты обратно в список. Короткая запись выглядит естественно:
Но у Stream.toList() есть важное свойство, он возвращает немодифицируемый список. Такой код не добавит третье имя:
Вместо изменения списка программа получит UnsupportedOperationException. То же произойдёт при remove или set.
Если нужен готовый результат только для чтения, это как раз удобно:
Вызывающий код не сможет случайно поменять состав labels. При этом сами элементы могут оставаться изменяемыми, не модифицируемость списка не делает глубоко неизменяемыми объекты внутри него.
Если список нужно дополнять после сбора, задайте изменяемую коллекцию явно:
Теперь add и remove доступны. Есть и вариант обернуть готовый результат:
Выбирайте способ сбора по тому, что будет происходить со списком дальше. Для чтения подойдёт toList(), для дальнейших изменений лучше явно создать ArrayList.
👉 Java Ready | #совет
При работе со Stream часто нужно собрать результаты обратно в список. Короткая запись выглядит естественно:
var names = Stream.of("Ana", "Bob").toList();Но у Stream.toList() есть важное свойство, он возвращает немодифицируемый список. Такой код не добавит третье имя:
names.add("Cyd");Вместо изменения списка программа получит UnsupportedOperationException. То же произойдёт при remove или set.
Если нужен готовый результат только для чтения, это как раз удобно:
List<String> labels = users.stream()
.map(User::name)
.toList();
Вызывающий код не сможет случайно поменять состав labels. При этом сами элементы могут оставаться изменяемыми, не модифицируемость списка не делает глубоко неизменяемыми объекты внутри него.
Если список нужно дополнять после сбора, задайте изменяемую коллекцию явно:
var names = Stream.of("Ana", "Bob")
.collect(Collectors.toCollection(ArrayList::new));Теперь add и remove доступны. Есть и вариант обернуть готовый результат:
var names = new ArrayList<>(stream.toList());
Выбирайте способ сбора по тому, что будет происходить со списком дальше. Для чтения подойдёт toList(), для дальнейших изменений лучше явно создать ArrayList.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥4