Изучение внутреннего устройства Git — это одна из топ-5 вещей, которые разработчик может сделать для себя.
Вся суть Git внезапно сводится к коммитам в ветках, и тогда становится видно, насколько Git красив и прост по своей сути.
- rebase — это просто пересоздание коммитов на новой базе
- cherry-pick — это просто повторное применение изменений одного коммита
- merge — это просто соединение историй
- reset — это просто перемещение текущего ref’а
На самом деле это одна из самых красивых систем, когда-либо созданных.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Проверяем подпись входящего webhook на Java!
Сначала сверяем HMAC-подпись провайдера с исходными байтами тела, а уже после проверки разбираем JSON.
Считаем ожидаемую подпись от сырого тела запроса:
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret, "HmacSHA256"));
byte[] expected = mac.doFinal(rawBody);
Секрет передаётся в байтах, а rawBody сохраняется без повторной сериализации. Даже изменение пробелов в JSON меняет HMAC, поэтому проверять нужно именно те байты, которые подписал отправитель.
Декодируем hex из заголовка и сравниваем подписи:
byte[] supplied = HexFormat.of().parseHex(header);
boolean valid = MessageDigest.isEqual(
expected, supplied);
MessageDigest.isEqual предназначен для сравнения секретных значений без раннего выхода по первому несовпавшему байту. Неверный формат заголовка нужно отдельно считать невалидной подписью, а не продолжать обработку запроса.
Принимаем только корректный заголовок:
try {
return verify(rawBody, secret, header);
} catch (IllegalArgumentException error) {
return false;
}
До успешной проверки не запускаем логику и не сохраняем событие. Дополнительно проверяем защиту от повторной доставки по идентификатору события.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Webhook — это обычный POST-запрос, который ваше приложение получает, когда в другой системе происходит определённое событие.
Создайте endpoint с @PostMapping для получения данных события.
Никакого polling (периодического опроса) — другая система сама отправляет запрос, когда событие происходит.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
🖥 Руководство по безопасному Java-коду помогает проверять границы доверия
Приложение получает данные от пользователей, читает файлы и обращается к внешним сервисам. В каждой такой точке нужно определить допустимые значения и действия.
В разборе Java Secure Coding Guidelines собраны рекомендации для безопасной разработки на Java.
Используйте их для небольшого ревью одного компонента. Проследите путь внешних данных от входа до операции, которую они запускают, и проверьте валидацию, доступ и обработку ошибок.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Паттерны проектирования, которые действительно пригодятся в работе с Java:
- Builder — создание объектов с большим количеством необязательных полей.
- Factory — скрывает логику выбора и создания конкретного класса.
- Strategy — позволяет менять поведение, например использовать разные способы оплаты.
- Singleton — гарантирует наличие одного экземпляра объекта (Spring-бин по умолчанию тоже является Singleton в рамках своего контейнера).
- Observer — работа с событиями и слушателями.
- Adapter — позволяет использовать старый API через новый интерфейс.
- Decorator — добавляет функциональность, не изменяя исходный класс.
- Template Method — задаёт общую последовательность шагов, позволяя менять детали реализации.
Необязательно заучивать все паттерны из книги.
Изучите эти — и вы начнёте замечать их в коде, с которым уже работаете.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
🖥 Разбор сбоя JVM начинается с сохранения диагностических данных
Процесс Java неожиданно завершился. Для поиска причины понадобятся сведения о runtime, окружении и том, что происходило перед сбоем.
В руководстве по первичной диагностике JVM показано, как подойти к такой ситуации и собрать данные для дальнейшего анализа.
Проверьте заранее, сохраняет ли ваше окружение журнал фатальной ошибки и другие диагностические файлы после перезапуска контейнера. Добавьте их сбор в инструкцию по разбору инцидентов.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Код на внимательность! Что выведет эта программа?
Посмотрите на фрагмент кода ниже. Как думаете, какой результат мы увидим в консоли? Отработает ли он вообще или упадет с ошибкой?
public class Main {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 500;
Integer d = 500;
System.out.println(a == b);
System.out.println(c == d);
}
}
Пишите ваши догадки в комментарии!
А правильный ответ и объяснение я спрятал под спойлером ниже:
Ответ:
true
false
Почему так?
Дело в Integer Cache. В Java объекты класса Integer автоматически кэшируются в диапазоне от -128 до 127.
Поэтому переменные a и b (значение 100) указывают на один и тот же закэшированный объект в памяти. Сравнение ссылок == для них выдаст true.
А вот значение 500 выходит за пределы кэша. Для c и d создаются два абсолютно разных объекта в куче. Сравнение их ссылок через == вернет false.
Мораль: чтобы сравнивать значения объектов-оберток в Java, всегда используйте .equals()! |
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Spring Boot API отвечает тайм-аутами, а PostgreSQL выглядит здоровым?
CPU в норме, медленных запросов нет. Причина может быть в исчерпании пула соединений HikariCP. Когда все соединения заняты, новые запросы ждут освобождения одного из них. Если время ожидания превышает connectionTimeout, получение соединения завершается ошибкой.
Особенно коварны долгие транзакции: приложение может удерживать соединение, пока ждёт внешний API, хотя сама база в этот момент почти не работает.
В статье разбираются:
• Причины исчерпания пула.
• Влияние долгих транзакций.
• Диагностика по метрикам HikariCP.
• Практические способы устранения проблемы.
Читать статью
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Класс Console
В JDK 6 и более поздних версиях мы можем использовать класс Console из пакета java.io для чтения и записи в консоль.
Если нам нужно прочитать конфиденциальную информацию, например пароль, мы можем использовать метод readPassword(), чтобы запросить у пользователя пароль и прочитать пароль из консоли с отключенным эхом.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Знаете, почему Collectors.toMap иногда завершается исключением?
Двухаргументный вариант подходит, когда ключи гарантированно уникальны. Если два объекта дают одинаковый ключ, сборщик не выбирает значение молча, а выбрасывает IllegalStateException.
Например, события могут повторять тип:
Map counts = events.stream()
.collect(Collectors.toMap(
Event::type, Event::id));
При повторе типа такой сбор завершится ошибкой. Для подсчёта задаём функцию объединения:
Map counts = events.stream()
.collect(Collectors.toMap(
Event::type, e -> 1L, Long::sum));
Long::sum вызывается только для повторного ключа, поэтому значения накапливаются. Такой вариант подходит для подсчёта событий, заказов или ошибок по категории.
Если вместо сводного значения нужно собрать объекты в группы, используем groupingBy:
Map byType = events.stream()
.collect(Collectors.groupingBy(Event::type));
Merge-функцию выбираем по смыслу данных, а не просто чтобы убрать исключение.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Эволюция Java: от Java 8 до Java 27
За последние годы Java заметно изменилась: язык получил функциональные возможности, улучшенную производительность, новые инструменты для многопоточности и более удобный синтаксис.
Ключевые обновления по версиям:
• Java 8 (2014) — лямбда-выражения, Stream API, функциональные интерфейсы и новый API для работы с датой и временем.
• Java 11 (2018, LTS) — стандартный HTTP Client, var в параметрах лямбда-выражений и запуск однофайловых программ без предварительной компиляции.
• Java 17 (2021, LTS) — sealed-классы, pattern matching для instanceof и усиленная инкапсуляция внутренних API JDK.
• Java 21 (2023, LTS) — виртуальные потоки, record patterns, pattern matching для switch и sequenced collections.
• Java 24 (2025) — улучшения виртуальных потоков: JEP 491 позволяет синхронизированным виртуальным потокам не блокировать carrier threads.
• Java 25 (2025, LTS) — новая LTS-версия с улучшениями производительности, безопасности и стабильности.
• Java 27 (2026) — очередной этап развития платформы: новые возможности и улучшения производительности, многопоточности и удобства разработки.
Что важно разработчикам? Java давно перестала быть просто языком для корпоративных приложений. Виртуальные потоки упрощают работу с большим количеством параллельных задач, Stream API помогает обрабатывать коллекции, а современные версии языка делают код выразительнее.
Если вы используете Java в работе, стоит следить за новыми версиями — особенно за LTS-релизами, которые получают длительную поддержку.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
📊 Buildpack задаёт базовую ОС и состав окружения Java-контейнера
Buildpack собирает образ из исходников без собственного Dockerfile. При этом выбранный builder определяет окружение сборки, а run image — основу контейнера, в котором будет работать приложение.
В сравнении реализаций buildpack собраны Paketo, Heroku, Google и другие варианты. Они различаются базовой ОС, поддержкой JVM и Native Image, доступными системными библиотеками и условиями сопровождения.
Перед выбором проверьте нативные зависимости приложения, сертификаты и архитектуру процессора. Затем соберите один и тот же сервис двумя подходящими вариантами и сравните размер образа, результаты сканирования и запуск в своём окружении.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Утечка памяти, фрагментация и нагрузка на GC: в чём разница?
Приложение потребляет много памяти — это ещё не означает утечку. Причины могут быть разными.
Memory Leak — ненужные объекты остаются достижимыми. Программа больше не использует их, но продолжает хранить ссылки, поэтому сборщик мусора не может освободить память.
Типичные причины: кэш без ограничений, растущие статические коллекции, неудалённые слушатели и неправильная работа с ThreadLocal. Один из признаков — устойчивый рост занятой памяти даже после сборок мусора.
Memory Fragmentation — свободная память есть, но она разбита на неудобные участки. Например, суммарно места достаточно, но нет подходящего непрерывного блока для выделения.
В Java влияние фрагментации зависит от сборщика мусора и области памяти: heap, native или off-heap. Многие сборщики перемещают объекты и уплотняют память, но это не устраняет все возможные случаи.
GC Pressure — объекты создаются быстрее, чем системе удобно их убирать. Память освобождается, однако GC приходится работать часто или интенсивно, расходуя CPU и увеличивая задержки.
Причины: высокая скорость выделения памяти, множество временных объектов, большие промежуточные структуры и частая сериализация.
При диагностике смотрите на разные сигналы:
• Утечка: какие объекты остаются живыми и что удерживает ссылки на них.
• Фрагментация: в какой области памяти не удаётся выделить нужный блок.
• GC Pressure: скорость аллокаций, частоту сборок, паузы и затраты CPU.
Увеличение -Xmx иногда помогает, но при утечке лишь откладывает проблему. Сначала выясните, почему память растёт и на что тратится время GC.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
🛠 JDK 28 сможет выделять небольшие блоки нативной памяти из повторно используемых пулов
Вызову нативной функции нужен указатель на одно число. Создавать полноценное выделение памяти ради нескольких байт может оказаться дороже самой работы с ними.
Типичный код FFM выглядит так:
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment =
arena.allocate(ValueLayout.JAVA_INT);
segment.set(ValueLayout.JAVA_INT, 0, 42);
}
В разрабатываемом JDK 28 небольшие выделения confined-арены могут обслуживаться из пула. Он запрашивается лениво, при первом подходящем выделении. По умолчанию кеш платформенного потока может хранить четыре пула по 64 байта.
Если размер или выравнивание не подходят, используется обычный путь.
🟡 Закрытая арена не становится доступной повторно
После close() её сегменты всё так же недоступны. Перед повторным использованием занятая часть пула очищается. Повторно используется внутренняя память, а не старые MemorySegment.
Менять исходный код ради этой оптимизации не требуется. Но это улучшение будущего JDK 28: пример API уже знаком по выпущенным версиям, описанный механизм пулов — ещё нет.
🔗 Как устроены пулы confined-арен
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
Spring Boot + Kafka: отправка и получение событий с минимумом кода
Внедрите KafkaTemplate, чтобы отправлять сообщения в топик.
Добавьте @KafkaListener к методу, чтобы обрабатывать входящие сообщения.
Автоконфигурация Spring Boot настроит компоненты Kafka по параметрам spring.kafka.*.
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪
☕️ JDK 27 копирует HashMap без повторного вычисления хешей в специальных сценариях
Копирование Map выглядит коротко:
var copy = new HashMap(source);
Но внутри общего пути могут быть итератор, обращения к ключам и значениям, повторный hashCode(). Когда через одну точку вызова проходят разные реализации Map, JIT сложнее убрать эти расходы.
В JDK 27 добавили специальные пути для источника, который является именно HashMap, и неизменяемой обёртки над HashMap. Они обходят исходную таблицу напрямую и используют хеши, уже сохранённые в её узлах.
Оптимизация относится и к putAll(). Для других реализаций Map остаётся общий путь.
🟡 Это не глубокое копирование
Новая таблица по-прежнему содержит ссылки на те же ключи и значения. Изменяемый объект внутри карты не станет независимым после создания копии.
В статье приведены заметные выигрыши микробенчмарков на конкретной платформе. Переносить эти проценты на весь сервис нельзя: сначала проверьте, есть ли копирование карт среди его горячих участков.
🔗 Что ускорили в библиотеках JDK 27
Полезные ресурсы по Java 👩💻| Наш канал в MAX 🟪