Библиотека Java разработчика
10.8K subscribers
1.14K photos
564 videos
58 files
1.44K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
📌 Stream.toList() vs Collectors.toList() — безопасная замена?

🧠 В Java 16+ появился метод Stream.toList(), который собирает элементы потока в список. Раньше мы писали:


List<String> list = stream.collect(Collectors.toList());


Теперь можно укоротить до:


List<String> list = stream.toList();


💡 Главные отличия:

1️⃣ Неизменяемость
toList() возвращает unmodifiable List — любые add()/remove() вылетят UnsupportedOperationException.
Если нужен изменяемый список, продолжайте использовать collect(Collectors.toList()) или


stream.collect(Collectors.toCollection(ArrayList::new));


2️⃣ Null-элементы
toList() не допускает null и бросит NPE при встрече null в потоке. Collectors.toList() сохранит null без ошибок.

3️⃣ Спецификация
Stream.toList() гарантированно создаёт новый список с точным размером, а Collectors.toList() лишь «может» вернуть любой List (часто ArrayList, но без чётких гарантий).

⚠️ Если вам важна мутабельность или поддержка nullне меняйте на toList().

Если же нужен чистый readonly-список и вы уверены в отсутствии nullсмело переходите на toList() для более лаконичного и потенциально более эффективного кода.

Я перехожу на toList() везде, где нужен только чтение — получилось короче и понятнее.

👉@BookJava
👍9
⁉️ Монолит или микросервисы? Руководство для архитекторов, которые ценят свои нервы

Приглашаем на открытый урок.

🗓 17 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Software Architect».

📌 Что будет на вебинаре:
✔️ Как не попасть в ловушку “модных” микросервисов;
✔️ Разбор признаков, что пора выходить из монолита;
✔️ Архитектурные паттерны для перехода к микросервисам (Strangler Fig, BFF, Self-contained systems);
✔️ Организационные и технические риски — что точно пойдёт не так и как это предсказать;
✔️ Роль DevOps, CI/CD и мониторинга в выборе архитектуры.

👥 Для кого этот вебинар:
- Разработчиков Backend и FullStack, участвующих в архитектурных решениях;
- Архитекторов ПО, которые планируют масштабирование приложений;
- Тимлидов и DevOps-инженеров, выстраивающих процесс разработки и доставки;
- Технических менеджеров, выбирающих стратегию развития продукта.

🎯 После вебинара вы:
- Получите пошаговое руководство по выбору архитектуры под ваш проект;
- Научитесь оценивать реальные риски и стоимость микросервисов;
- Поймёте, как внедрять архитектурные изменения без сбоев и хаоса;
- Увидите, как принимать взвешенные архитектурные решения, сохраняя технический контроль и производительность команды.

💡 Идеальный вебинар для тех, кто хочет перестать "архитектурить на ощущениях" и начать действовать стратегически.

🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06

👉 Регистрация на вебинар: https://vk.cc/cMHSzz

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
2👎1
📌 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

2. Реализуйте 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());

3. Сборка и запуск:


# Сохраняем 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


⚠️ Ограничения и нюансы

* Не все native-библиотеки безопасны для снапшота.
* Тяжёлые background-потоки: до checkpoint лучше останавливать.
* Проверяйте на staging-окружении — subtle bugs могут всплыть только после restore.

📌 Зачем это нужно?

* 🚀 Ускоренный cold-start для Spring Boot 3+ сервисов (лучшая DevOps-интеграция в контейнерах и serverless).
* 💰 Экономия ресурсов в автоскейлируемых кластерах.

Простой CRaC-proof-of-concept позволит вам измерить прирост старта ваших микросервисов уже сегодня!

👉@BookJava
👍51
👩‍💻 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 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’ов


Это позволит драйверу посылать пачками, а Hibernate — сортировать операции для максимальной эффективности.

💡 Сниппет для batch-пакетов


@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() освобождает ОЗУ от управляемых сущностей.

⚠️ Важные моменты

* GenerationType.IDENTITY отключает batching. Используйте @SequenceGenerator с allocationSize.
* При двусторонних связях (OneToMany) избегайте каскадного сохранения огромных графов — лучше сохранять “плоско” и затем связывать.
* Следите за JDBC-драйвером: не все поддерживают batch-вставки одинаково хорошо.

💡 Совет по мониторингу
Запустите приложение с -Dorg.hibernate.SQL=DEBUG и -Dhibernate.format_sql=true — вы увидите групповые INSERT вместо множества одиночных.

📌 Результат

* Скорость записи растёт в 5–10× (в зависимости от нагрузки).
* Память на стороне приложения остаётся стабильной, без роста Persistence Context.

👉@BookJava
👍6
🧠 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) без копирования — рискуете нарушить неизменяемость!

👉@BookJava
👍5
VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер!

28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных.

Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные!

Подробности — на сайте.
💩4👍2
🧠 Коллекторы и toList() в Java 16+: можно ли заменить collect(Collectors.toList()) на просто .toList()?

Да, но с нюансами.

📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:


List<String> list = stream.collect(Collectors.toList());


на:


List<String> list = stream.toList();


💡 Но будь осторожен. Вот 3 ключевых отличия:


1️⃣ Немодифицируемость

* .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



2️⃣ Тип возвращаемого списка

* Collectors.toList() — это ArrayList (или его сабкласс).
* .toList() — это неопределённый тип внутри JDK (часто List.of под капотом).

Если ты делаешь что-то вроде:


if (list instanceof ArrayList) ...


то поведение может измениться.


3️⃣ Параллельные стримы

.toList() оптимизирован для параллельных стримов: может работать быстрее, но также может повлиять на порядок, если ты этого явно не контролируешь.


⚠️ Когда НЕ стоит заменять:

* Если ты мутируешь список после получения.
* Если ты полагаешься на конкретный тип (например, ArrayList).
* Если ты используешь Java < 16.


Когда заменить можно:

* Если тебе нужен read-only список.
* Если ты не изменяешь коллекцию.
* Если важна сжатость и выразительность.


📌 Резюме:

Заменяй collect(Collectors.toList()) на .toList(), только если тебе действительно не нужен изменяемый список. Это безопасно при соблюдении условий, но может привести к неожиданным багам в тестах и проде, если забыть про неизменяемость.


👉@BookJava
👍51
🧠 Осторожно с @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
👍93
📚 Продвинутые методы архивации: LZ77/78

Приглашаем на открытый урок.

🗓 25 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».

✔️ На этом вебинаре мы завершим создание архиватора, добавив алгоритм LZ77/78. Разберем принцип словарного сжатия, механизм поиска повторяющихся последовательностей и формат их кодирования.

✔️ Имплементируем выбранный алгоритм и проведем финальное сравнение всех трех методов сжатия (RLE, Huffman, LZ77/78). Определим, какие алгоритмы лучше работают для различных типов файлов и почему.

Завершающее практическое занятие для тех, кто хочет освоить продвинутые алгоритмы и увидеть их применение в реальном проекте.​​​​​​​​​​​​​​​​

🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5

👉 Регистрация на вебинар: https://vk.cc/cMXtZN

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Вопросы-ответы собеседования

Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?

источник

👉@BookJava
👍71