📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
Совет 💡
Если вы хотите получить сообщение о первопричине, вы можете легко и безопасно получить его с помощью Apache Commons ExceptionUtils
. Методы getRootCauseMessage(Exception ex)
выдают сообщение в виде {ClassNameWithoutPackage} {ThrowableMessage}
👉@BookJava
🧠 Трюк с @EventListener
в Spring Boot — неочевидная ловушка
Когда ты используешь @EventListener
для обработки событий в Spring, ты можешь попасть в баг, который очень сложно отловить в бою — пропущенные события.
📌 Пример:
@Component
public class MyListener {
@EventListener
public void on(MyEvent event) {
// логика
}
}
MyListener
бин ещё не проинициализирован, а событие уже публикуется (ApplicationEventPublisher#publishEvent
), то метод on
просто не будет вызван. Spring не будет "накапливать" события для будущих слушателей.@PostConstruct
или в CommandLineRunner
, а слушатель находится в другом бине, который ещё не загружен.@DependsOn
.ApplicationReadyEvent
:
@Component
public class Publisher {
private final ApplicationEventPublisher publisher;
public Publisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
publisher.publishEvent(new MyEvent(this));
}
}
ApplicationRunner
или InitializingBean
, где порядок можно контролировать проще.💡 Почему в Set.of()
в Java нельзя добавить дубликаты и null
?
Когда используешь Set.of(...)
, можно столкнуться с двумя неожиданностями:
1. ❌ Нельзя добавлять дубликаты
2. ❌ Нельзя добавлять null
Разбираемся, почему так:
🔐 Set.of(...)
— это immutable Set
Метод Set.of(...)
, добавленный в Java 9, создаёт неизменяемое множество. Это значит:
* После создания ты не можешь изменить его (добавить, удалить элемент).
* Все элементы внутри должны быть уникальны и не должны быть null
.
📛 Почему нельзя дубликаты?
Потому что Set
по определению — это коллекция уникальных элементов.
А Set.of(...)
бросает IllegalArgumentException
, сразу во время создания, если переданы дубликаты:
Set.of("a", "b", "a"); // 💥 Бросит исключение!
null
?Set.of(...)
не принимает null
, потому что он реализован через внутренние immutable структуры, которые не допускают null`-значений. При попытке добавить `null
получишь NullPointerException
.
Set.of("a", null); // 💥 NullPointerException
null
может вести к неочевидным багам и плохо сочетается с концепцией неизменяемых коллекций.Set
, который принимает null
и дубликаты фильтрует сам? Используй HashSet
:
Set<String> set = new HashSet<>();
set.add(null); // ✅ Можно
📚 Продвинутые методы архивации: LZ77/78
Приглашаем на открытый урок.
🗓 25 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
✔️ На этом вебинаре мы завершим создание архиватора, добавив алгоритм LZ77/78. Разберем принцип словарного сжатия, механизм поиска повторяющихся последовательностей и формат их кодирования.
✔️ Имплементируем выбранный алгоритм и проведем финальное сравнение всех трех методов сжатия (RLE, Huffman, LZ77/78). Определим, какие алгоритмы лучше работают для различных типов файлов и почему.
Завершающее практическое занятие для тех, кто хочет освоить продвинутые алгоритмы и увидеть их применение в реальном проекте.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMXtZN
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Коллекторы и toList()
в Java 16+: можно ли заменить collect(Collectors.toList())
на просто .toList()
?
Да, но с нюансами.
📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:
List<String> list = stream.collect(Collectors.toList());
List<String> list = stream.toList();
.toList()
возвращает немодифицируемый список (immutable).Collectors.toList()
возвращает modifiable ArrayList.
var list1 = List.of("a", "b");
var list2 = list1.stream().toList();
list2.add("c"); // 💥 UnsupportedOperationException
var list3 = list1.stream().collect(Collectors.toList());
list3.add("c"); // ✅ OK
Collectors.toList()
— это ArrayList (или его сабкласс)..toList()
— это неопределённый тип внутри JDK (часто List.of
под капотом).
if (list instanceof ArrayList) ...
.toList()
оптимизирован для параллельных стримов: может работать быстрее, но также может повлиять на порядок, если ты этого явно не контролируешь.ArrayList
).Заменяйcollect(Collectors.toList())
на.toList()
, только если тебе действительно не нужен изменяемый список. Это безопасно при соблюдении условий, но может привести к неожиданным багам в тестах и проде, если забыть про неизменяемость.
🧠 Record (Java 16+) + pattern matching для instanceof
(Java 14+) в Java 17+ позволяют писать лаконичный и безопасный код:
📌 Запись DTO с валидацией через компактный конструктор:
public record User(String name, String email) {
public User {
Objects.requireNonNull(name, "name не должен быть null");
if (!email.contains("@")) {
throw new IllegalArgumentException("Неверный email: " + email);
}
}
}
toString()
, equals()
, hashCode()
без лишнего кода.
Object obj = …;
if (obj instanceof User u) {
System.out.println("Привет, " + u.name());
}
public record Team(String name, List<String> members) {
public Team {
members = List.copyOf(members);
}
}
members
нельзя изменить извне.public record X(List<String> list)
без копирования — рискуете нарушить неизменяемость!👩💻 JPQL: как писать запросы, которые не сломают Hibernate
Узнайте, как писать JPQL-запросы, которые ускорят Hibernate в 5 раз, избегая критических ошибок, тормозящих 80% проектов!
Приглашаем на открытый урок
🗓 19 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional».
🎯 О чём поговорим:
✔️- JPQL vs SQL: почему ваши запросы ломают Hibernate и как их переписать так, чтобы БД не «умирала» под нагрузкой.
✔️ Тайные ловушки: антипаттерны JPQL, генерирующие N+1 SELECT и тормозящие приложение, и методы их поиска в коде.
✔️ Оптимизация на максимум: как использовать JOIN FETCH, подзапросы и кэширование в JPQL для мгновенного ускорения Hibernate.
👥 Кому будет интересно:
Java-разработчикам, использующим Hibernate, системным архитекторам и инженерам по оптимизации производительности.
💡В результате урока вы:
Научитесь писать эффективные JPQL-запросы, избегать распространённых ошибок и значительно ускорять работу Hibernate-приложений.
🎁 Дарим промокод, который дает скидку на обучение - JAVA_06
🔗 Ссылка на регистрацию: https://vk.cc/cMKvog
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
⁉️ Монолит или микросервисы? Руководство для архитекторов, которые ценят свои нервы
Приглашаем на открытый урок.
🗓 17 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Software Architect».
📌 Что будет на вебинаре:
✔️ Как не попасть в ловушку “модных” микросервисов;
✔️ Разбор признаков, что пора выходить из монолита;
✔️ Архитектурные паттерны для перехода к микросервисам (Strangler Fig, BFF, Self-contained systems);
✔️ Организационные и технические риски — что точно пойдёт не так и как это предсказать;
✔️ Роль DevOps, CI/CD и мониторинга в выборе архитектуры.
👥 Для кого этот вебинар:
- Разработчиков Backend и FullStack, участвующих в архитектурных решениях;
- Архитекторов ПО, которые планируют масштабирование приложений;
- Тимлидов и DevOps-инженеров, выстраивающих процесс разработки и доставки;
- Технических менеджеров, выбирающих стратегию развития продукта.
🎯 После вебинара вы:
- Получите пошаговое руководство по выбору архитектуры под ваш проект;
- Научитесь оценивать реальные риски и стоимость микросервисов;
- Поймёте, как внедрять архитектурные изменения без сбоев и хаоса;
- Увидите, как принимать взвешенные архитектурные решения, сохраняя технический контроль и производительность команды.
💡 Идеальный вебинар для тех, кто хочет перестать "архитектурить на ощущениях" и начать действовать стратегически.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06
👉 Регистрация на вебинар: https://vk.cc/cMHSzz
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
⚡️ Быстрые альтернативы HashMap: EnumMap, массивы и примитивные коллекции
🧠 EnumMap вместо HashMap (enum-ключи)
📌 EnumMap<K, V>
хранит данные в массиве → нет хеширования и boxing’а.
EnumMap<Status, String> map = new EnumMap<>(Status.class);
map.put(Status.STARTED, "Запущен");
String status = map.get(Status.STARTED);
[0…MAX]
и известны заранее.
int MAX = 1000;
var cache = new String[MAX + 1];
cache[42] = "ответ";
String result = cache[42];
var fastMap = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<String>();
fastMap.put(42, "ответ");
String res = fastMap.get(42);
switch
, а не Map.
String handle(int code) {
return switch (code) {
case 100 -> "OK";
case 200 -> "Created";
default -> "Other";
};
}
switch
в tableswitch
или lookupswitch
— молниеносно.🚀 Spring WebFlux с Server-Sent Events 🚀
Улучшите свои приложения в режиме реального времени с помощью #SpringWebFlux и Server-Sent Events! 🔥
👉@BookJava
🎯 Java-хаки: динамический вывод с помощью printf()
Статья посвящена методу printf() в Java, который используется для создания форматированной строки вывода. В ней рассматривается синтаксис метода, различные спецификаторы формата (например, %d, %f, %s и т.д.) и то, как с их помощью управлять отображением чисел, строк и других типов данных в консоли.
Приведены примеры использования System.out.printf()
с пояснениями по флагам, ширинe и точности форматирования, а также показано, как легко создавать динамический и читаемый вывод в приложениях на Java.
https://springframework.guru/java-output-printf-method/
👉@BookJava
Как масштабировать машинные модели и работать с огромными объемами данных? Откройте для себя возможности Spark ML на открытом уроке от OTUS!
Spark ML — это мощный инструмент для масштабируемого машинного обучения, который позволяет обучать модели на больших данных, не переходя на специализированные ML-системы. Мы покажем, как интеграция с Spark SQL и DataFrame API упрощает ETL-подготовку данных и фичуризацию для реальных проектов.
Убедитесь, как Spark ML решает задачи отказоустойчивости и распределённых вычислений, позволяя вам легко строить промышленные ML-пайплайны.
Посетите открытый урок 11 июня в 20:00 МСК в преддверие старта курса «Spark Developer» и получите скидку на обучение: https://vk.cc/cMyLJ3
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
👩💻 Java — один из самых востребованных языков, но не каждый разработчик умеет использовать его возможности по максимуму.
На курсе «Java Developer. Professional» вы научитесь создавать современные Java-приложения, освоите Spring WebFlux и Kafka, а также разберётесь в работе JVM изнутри.
Пройдите тест, проверьте, достаточно ли у вас знаний для обучения на курсе:.
🎁 Дарим промокод, который дает скидку на обучение - JAVA_06
На курсе вас ждёт практическая работа с кодом, детальные разборы, ревью от экспертов и подходы, позволяющие писать эффективный и чистый код.
Начните свой путь к уровню Middle+ и используйте Java на 100%.
➡️ Пройти вступительный тест курса: https://vk.cc/cMxZwr
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Optional.stream()
появился в Java 9 и позволяет превратить Optional<T>
в Stream<T>
длины 0 или 1. Зачем это нужно?
📌 Когда применять?
◾️ 🧠 Интеграция с цепочками Stream API: если у вас есть коллекция Optional<T>
, можно собрать все непустые значения без дополнительных проверок:
List<Optional<User>> userOptionals = …;
List<User> users = userOptionals.stream()
.flatMap(Optional::stream) // из каждого Optional либо 1 элемент, либо пусто
.collect(Collectors.toList());
Optional.stream()
пришлось бы делать что-то вроде filter(Optional::isPresent).map(Optional::get)
.Optional<Something>
и вы хотите «сливать», а не оставлять пустые обёртки.Если вы строите конвейер обработки данных, а на каком-то шаге может не быть значения — Optional.stream()
поможет аккуратно пропустить «пустышки» и не ломать последующие операции.
Optional
, — использование stream()
создаёт впечатление, что у вас реально коллекция элементов, хотя всего лишь 1 или 0. Для простых случаев ifPresent()
, map()
, orElse()
читается понятнее.
// Менее канонично:
optionalValue.stream().forEach(v -> doSomething(v));
// Лучше:
optionalValue.ifPresent(v -> doSomething(v));
Optional.stream()
создаёт объект стрима и небольшую внутреннюю структуру, что на горячем участке кода (в tight loop) может сказаться на производительности. Если вместо него можно обойтись map().orElse()
, задумайтесь о легковесном варианте.Optional.stream()
в одиночном случае (не в контексте объединения множества опционалов), код может стать менее очевидным. Например:
// Что тут происходит?
Stream.of(opt1, opt2, opt3)
.flatMap(Optional::stream)
.findFirst();
Optional<User> result = opt1.isPresent() ? opt1
: opt2.isPresent() ? opt2
: opt3;
flatMap
и filter
без промежуточных Optional
.
List<Order> orders = getOrders();
// Для каждого заказа пытаемся получить пользователя из БД,
// но он может быть не найден (Optional<User>).
List<Optional<User>> maybeUsers = orders.stream()
.map(o -> userRepository.findById(o.getUserId()))
.toList();
// Теперь формируем список уже «существующих» юзеров:
List<User> users = maybeUsers.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Optional.stream()
полностью оправдан: сразу избавляемся от «пустых» опционалов.Не используйте Optional.stream()
внутри метода, который ожидает ровно одно значение или бросает исключение, если опционал пуст.
// Плохо:
User user = optionalUser.stream()
.findFirst()
.orElseThrow(() -> new NotFoundException("User not found"));
// Лучше так:
User user = optionalUser
.orElseThrow(() -> new NotFoundException("User not found"));
Optional.stream()
только когда действительно нужно объединить несколько Optional-ов в один Stream и пропустить пустые.🧠 Конфигурация Spring Boot 3 через record
и @ConstructorBinding
Вместо традиционных @Data
+ пустого конструктора можно сразу использовать Java 17 record
для настройки свойств:
📌 Почему это полезно?
🔴Полная иммутабельность: поля конфигов больше нельзя случайно перезаписать.
🔴Минимум «шаблонного» кода: не нужны геттеры, сеттеры, toString()
, equals()
и т.д.
🔴Чёткая связь с Java 17+ и актуальными best practices.
💡 Как сделать:
1. Подключаем зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
record
с аннотацией:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
@ConstructorBinding
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(
String host,
int port,
String username,
String password
) {}
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class AppConfig { }
application.yml
(или .properties
):
app:
mail:
host: smtp.example.com
port: 587
username: user@example.com
password: secret123
@ConstructorBinding
Spring не сможет смотать значения в record
’ы.@Validated
и JSR-303 аннотации (@NotNull
, @Min
и т.д.).record
заменил класс с 4 полями, геттерами и конструктором.record’а с разными префиксами или используйте @Profile
. В Spring Boot 3 этот подход «из коробки» работает наилучшим образом.🚀 Подборка Telegram каналов для программистов
Системное администрирование, DevOps 📌
/channel/bash_srv Bash Советы
/channel/win_sysadmin Системный Администратор Windows
/channel/sysadmin_girl Девочка Сисадмин
/channel/srv_admin_linux Админские угодья
/channel/linux_srv Типичный Сисадмин
/channel/devopslib Библиотека девопса | DevOps, SRE, Sysadmin
/channel/linux_odmin Linux: Системный администратор
/channel/devops_star DevOps Star (Звезда Девопса)
/channel/i_linux Системный администратор
/channel/linuxchmod Linux
/channel/sys_adminos Системный Администратор
/channel/tipsysdmin Типичный Сисадмин (фото железа, было/стало)
/channel/sysadminof Книги для админов, полезные материалы
/channel/i_odmin Все для системного администратора
/channel/i_odmin_book Библиотека Системного Администратора
/channel/i_odmin_chat Чат системных администраторов
/channel/i_DevOps DevOps: Пишем о Docker, Kubernetes и др.
/channel/sysadminoff Новости Линукс Linux
1C разработка 📌
/channel/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С
/channel/DevLab1C 1С:Предприятие 8
/channel/razrab_1C 1C Разработчик
/channel/buh1C_prog 1C Программист | Бухгалтерия и Учёт
/channel/rabota1C_rus Вакансии для программистов 1С
Программирование C++📌
/channel/cpp_lib Библиотека C/C++ разработчика
/channel/cpp_knigi Книги для программистов C/C++
/channel/cpp_geek Учим C/C++ на примерах
Программирование Python 📌
/channel/pythonofff Python академия.
/channel/BookPython Библиотека Python разработчика
/channel/python_real Python подборки на русском и английском
/channel/python_360 Книги по Python
Java разработка 📌
/channel/BookJava Библиотека Java разработчика
/channel/java_360 Книги по Java Rus
/channel/java_geek Учим Java на примерах
GitHub Сообщество 📌
/channel/Githublib Интересное из GitHub
Базы данных (Data Base) 📌
/channel/database_info Все про базы данных
Мобильная разработка: iOS, Android 📌
/channel/developer_mobila Мобильная разработка
/channel/kotlin_lib Подборки полезного материала по Kotlin
Фронтенд разработка 📌
/channel/frontend_1 Подборки для frontend разработчиков
/channel/frontend_sovet Frontend советы, примеры и практика!
/channel/React_lib Подборки по React js и все что с ним связано
Разработка игр 📌
/channel/game_devv Все о разработке игр
Библиотеки 📌
/channel/book_for_dev Книги для программистов Rus
/channel/programmist_of Книги по программированию
/channel/proglb Библиотека программиста
/channel/bfbook Книги для программистов
БигДата, машинное обучение 📌
/channel/bigdata_1 Big Data, Machine Learning
Программирование 📌
/channel/bookflow Лекции, видеоуроки, доклады с IT конференций
/channel/rust_lib Полезный контент по программированию на Rust
/channel/golang_lib Библиотека Go (Golang) разработчика
/channel/itmozg Программисты, дизайнеры, новости из мира IT
/channel/php_lib Библиотека PHP программиста 👨🏼💻👩💻
/channel/nodejs_lib Подборки по Node js и все что с ним связано
/channel/ruby_lib Библиотека Ruby программиста
/channel/lifeproger Жизнь программиста. Авторский канал.
QA, тестирование 📌
/channel/testlab_qa Библиотека тестировщика
Шутки программистов 📌
/channel/itumor Шутки программистов
Защита, взлом, безопасность 📌
/channel/thehaking Канал о кибербезопасности
/channel/xakep_2 Хакер Free
Книги, статьи для дизайнеров 📌
/channel/ux_web Статьи, книги для дизайнеров
Математика 📌
/channel/Pomatematike Канал по математике
/channel/phis_mat Обучающие видео, книги по Физике и Математике
/channel/matgeoru Математика | Геометрия | Логика
Excel лайфхак📌
/channel/Excel_lifehack
/channel/mir_teh Мир технологий (Technology World)
Вакансии 📌
/channel/sysadmin_rabota Системный Администратор
/channel/progjob Вакансии в IT
🤯 🤯 🤯 Параллелизм в многопоточном Java-коде создаёт новые проблемы в тестировании, а баги остаются незамеченными?
⚡️ Приглашаем на открытый вебинар «Юнит тесты для многопоточного кода»
24 июня в 20:00 МСК.
На вебинаре мы разберём:
✔️ Как обнаружить гонки, дедлоки и нестабильность в многопоточном коде.
✔️ Как использовать argumentCaptor и spy для проверки взаимодействия потоков.
✔️ Эмуляцию задержек и таймингов с помощью AdditionalAnswers.
🦾 После урока вы будете уверенно писать стабильные unit-тесты для многопоточного кода, выявлять скрытые баги и улучшать качество тестирования.
Открытый урок проходит в преддверии старта курса «Java Developer. Advanced».
Все участники получат скидку на обучение.
🔗 Регистрируйтесь прямо сейчас: https://vk.cc/cMZNzb
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Вопросы-ответы собеседования
Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?
источник
👉@BookJava
🧠 Осторожно с @Transactional
на private-методах!
Очень частый анти-паттерн, который легко упустить 👇@Service
public class UserService {
@Transactional
private void saveUser(User user) {
userRepository.save(user);
}
public void create() {
saveUser(new User());
}
}
Кажется, всё ок. Но ❌ транзакция НЕ работает.
📌 Почему?
Spring AOP использует прокси, а прокси не “видит” вызовы private-методов внутри класса. Такие вызовы происходят напрямую, мимо прокси-обёртки — и аннотация @Transactional
просто игнорируется.
💡 Решение:
1. Сделай метод public и вызывай его извне (или из другого бина).
2. Или выдели этот метод в отдельный бин-сервис.
Пример:@Service
public class UserTransactionalHelper {
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
}
И в UserService:public void create() {
helper.saveUser(new User());
}
⚠️ Так же не работают protected, private, final, static, и @PostConstruct
-методы.
Нужно помнить: Spring AOP = прокси, а значит, работают только публичные методы, вызываемые ИЗВНЕ.
👉@BookJava
VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер!
28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных.
Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные!
Подробности — на сайте.
🧠 JPA Batch Insert: ускоряем и защищаем от OOM
📌 Настройка Hibernate
Добавьте в application.yml
или properties
:
spring:
jpa:
properties:
hibernate.jdbc.batch_size: 50 # размер пакета
hibernate.order_inserts: true # группировка INSERT’ов
hibernate.order_updates: true # группировка UPDATE’ов
@Service
@RequiredArgsConstructor
public class OrderService {
private final EntityManager em;
private static final int BATCH_SIZE = 50;
@Transactional
public void saveAll(List<Order> orders) {
for (int i = 0; i < orders.size(); i++) {
em.persist(orders.get(i));
if (i > 0 && i % BATCH_SIZE == 0) {
em.flush();
em.clear(); // освобождаем persistence-context
}
}
em.flush();
em.clear();
}
}
flush()
выталкивает пакеты в БД,clear()
освобождает ОЗУ от управляемых сущностей.@SequenceGenerator
с allocationSize
.-Dorg.hibernate.SQL=DEBUG
и -Dhibernate.format_sql=true
— вы увидите групповые INSERT
вместо множества одиночных.📌 CRaC (Coordinated Restore at Checkpoint) — горячая JVM-фишка для сверхбыстрого cold-start: сохраняем состояние приложения после инициализации и моментально «восстанавливаем» при рестарте.
🧠 Как это работает
1. JVM создаёт снимок (checkpoint) всего heap- и native-состояния сразу после bootstrap и bean-инициализации.
2. При рестарте JVM грузит этот снимок вместо полной загрузки классов и прогрева JIT.
💡 Подключение в Java 21+
1. Включите экспериментальный модуль:
--add-modules jdk.crac
--enable-preview
CheckpointListener
для чистки и восстановления ресурсов:
import jdk.crac.Core;
import jdk.crac.Control;
import jdk.crac.CheckpointListener;
import jdk.crac.Context;
import org.springframework.stereotype.Component;
@Component
public class CracHandler implements CheckpointListener {
@Override
public void beforeCheckpoint(Context<?> ctx) {
// 📌 Закрываем пулы, Flush в БД, отписываемся от очередей
}
@Override
public void afterRestore(Context<?> ctx) {
// 💡 Реинициализируем пулы, повторная регистрация listeners
}
}
// Регистрация слушателя
Core.getGlobalContext().register(new CracHandler());
# Сохраняем checkpoint
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCCheckpointToDir=crac-checkpoint \
-jar app.jar
# Восстанавливаем из него
java \
--add-modules jdk.crac \
--enable-preview \
-XX:CRaCRestoreFrom=crac-checkpoint \
-jar app.jar
📌 Stream.toList() vs Collectors.toList() — безопасная замена?
🧠 В Java 16+ появился метод Stream.toList()
, который собирает элементы потока в список. Раньше мы писали:
List<String> list = stream.collect(Collectors.toList());
List<String> list = stream.toList();
toList()
возвращает unmodifiable List
— любые add()/remove()
вылетят UnsupportedOperationException
.collect(Collectors.toList())
или
stream.collect(Collectors.toCollection(ArrayList::new));
toList()
не допускает null
и бросит NPE при встрече null
в потоке. Collectors.toList()
сохранит null
без ошибок.Stream.toList()
гарантированно создаёт новый список с точным размером, а Collectors.toList()
лишь «может» вернуть любой List
(часто ArrayList
, но без чётких гарантий).toList()
.null
— смело переходите на toList()
для более лаконичного и потенциально более эффективного кода.toList()
везде, где нужен только чтение — получилось короче и понятнее.Test Driven Development (TDD) in Java
Creating a Queue Abstract Data Type class
Introduction to Test Doubles Dummies and Stubs
Introduction to Test Doubles Spies
Introduction to Test Doubles Mocks
Introduction to Test Doubles - Fakes
источник
👉@BookJava
📚 Эффективное сжатие текста: код Хаффмана в действии
Приглашаем на открытый урок.
🗓 11 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
На этом вебинаре мы продолжим разработку архиватора, реализовав код Хаффмана.
✔️ Рассмотрим, как построить дерево кодов, где частота появления символов определяет их битовое представление.
✔️ Интегрируем алгоритм в наш архиватор и проведем сравнительное тестирование с RLE.
✔️ Увидим, как эффективно работает код Хаффмана на текстовых файлах и других типах данных.
Отличная возможность изучить продвинутые древовидные структуры данных на практическом примере.
Развивайте алгоритмическое мышление, увеличивайте производительность программ.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMzyUv
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Структурированное логирование в Spring Boot 3.5.5
Spring Boot 3.5.5 приносит улучшенное структурированное логирование.
Чтобы его включить, добавьте следующее в ваш application.yml
:
Это обеспечивает более чистые, структурированные логи, что делает их проще для разбора инструментами вроде ELK, Grafana или Datadog.
👉@BookJava
Bulkhead — это паттерн из мира устойчивых систем, цель которого — изолировать сбои в одном компоненте, чтобы они не “затопили” всю систему. Сейчас я покажу вам несколько способов его реализации и подсвечу неочевидный момент при работе с любыми паттернами.
🧠 Концепция: представьте корабль с отсековыми переборками (bulkheads). Если вода просачивается в один отсек, остальные остаются сухими, и судно всё ещё может плыть. В мире Java/Spring это означает: ограничивать ресурсы (пулы потоков, соединения, очереди) для каждого сервиса/метода, чтобы при пике нагрузки или ошибках нагрузка не разошлась по всей системе.
📌 Способ 1: отдельные пул-экзекьюторы
@Configuration
public class BulkheadConfig {
@Bean("serviceAExecutor")
public ThreadPoolTaskExecutor serviceAExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("svcA-");
executor.initialize();
return executor;
}
}
@Service
public class ServiceA {
@Autowired @Qualifier("serviceAExecutor")
private Executor executor;
public CompletableFuture<String> callExternal() {
return CompletableFuture.supplyAsync(() -> {
// долгий/ненадежный вызов
return externalClient.fetchData();
}, executor);
}
}
RejectedExecutionHandler
по необходимости.
resilience4j.bulkhead.instances:
myServiceBulkhead:
maxConcurrentCalls: 5
maxWaitDuration: 100ms
@Service
public class MyService {
private final Bulkhead bulkhead;
public MyService(BulkheadRegistry registry) {
this.bulkhead = registry.bulkhead("myServiceBulkhead");
}
public String process() {
return Bulkhead.decorateSupplier(bulkhead, () -> {
// защищенный вызов
return externalClient.process();
}).get();
}
}
maxConcurrentCalls
слишком маленьким, часть запросов будет сразу отвергаться с BulkheadFullException
. Неочевидный момент: нужно мониторить реальную нагрузку и подбирать значения, а не копировать из гугла.
@Bulkhead(name = "myServiceBulkhead", type = Bulkhead.Type.SEMAPHORE)
public String annotatedProcess() { … }
resilience4j.bulkhead.instances:
myThreadPoolBulkhead:
maxThreadPoolSize: 10
queueCapacity: 20
@Bulkhead(name = "myThreadPoolBulkhead", type = Bulkhead.Type.THREADPOOL)
public CompletionStage<String> asyncProcess() { … }
30 лет Java: от провалившегося гаджета до фундамента разработки ПО
Некоторые языки программирования, например Rust, Go и TypeScript, считаются крутыми. Другие, в том числе Cobol и Java, «скучны». Однако пусть Java, которому 23 мая этого года исполнилось тридцать лет, может, и не самый захватывающий язык, он остаётся одним из самых важных.
Путь Java начался 23 мая 1995 года, когда его выпустила компания Sun Microsystems. За прошедшее время благодаря удачному видению разработчиков и адаптивности он превратился из нишевого проекта для потребительской электроники в мощный фундамент энтерпрайз-, облачной и веб-разработки.
Хоть Java исполнилось тридцать, его история гораздо дольше. Корнями этот язык уходит в 1991 год, когда инженеры Sun Джеймс Гослинг, Майк Шеридан и Патрик Ноутон приступили к созданию языка для интерактивного телевидения и встроенных устройств. Этот проект назвали Green Project. Его цель заключалась не столько в написании нового языка, сколько в создании того, что бы мы сегодня назвали контроллером Интернета вещей. Ещё один разработчик Java Тим Линдхольм, описал его как «своего рода гибрид между КПК и универсальным пультом дистанционного управления».
https://habr.com/ru/articles/914970/
original https://www.zdnet.com/article/java-at-30-how-a-language-designed-for-a-failed-gadget-became-a-global-powerhouse/
👉@BookJava
🔍Тестовое собеседование с Java-разработчиком из Т1 Иннотех уже завтра
4 июня(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.
Как это будет:
📂 Илья Аров, старший разработчик в Т1, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Илья будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Илье
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама. ООО "ШОРТКАТ", ИНН: 9731139396, erid: 2VtzqvL6bDw
Релиз через два дня. Код готов. Почти...
Остались тесты. Ну, точнее — покрытие. Потому что QA уже дышит в затылок, а ты сидишь и выбираешь: спать или корпеть до утра.
Explyt Test умеет создавать тесты под твой код — сам. Быстро. В IDE. Без плясок.
Хочешь, чтобы релиз прошёл, а не пролетел? Попробуй бесплатно! 👉 explyt.ai