uk
Feedback
Библиотека Java разработчика

Библиотека Java разработчика

Відкрити в Telegram

📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Показати більше

📈 Аналітичний огляд Telegram-каналу Библиотека Java разработчика

Канал Библиотека Java разработчика (@bookjava) у мовному сегменті Російська є активним учасником. На даний момент спільнота об'єднує 10 115 підписників, посідаючи 11 697 місце в категорії Технології та додатки та 62 901 місце у регіоні Росія.

📊 Показники аудиторії та динаміка

З моменту свого створення невідомо, проект продемонстрував стрімке зростання, зібравши аудиторію у 10 115 підписників.

За останніми даними від 27 серпня, 2026, канал демонструє стабільну активність. Хоча за останні 30 днів спостерігається зміна кількості учасників на -73, а за останні 24 години на -3, загальне охоплення залишається високим.

  • Статус верифікації: Не верифікований
  • Рівень залученості (ER): Середній показник залученості аудиторії становить 7.20%. Протягом перших 24 годин після публікації контент зазвичай збирає 3.71% реакцій від загальної кількості підписників.
  • Охоплення публікацій: В середньому кожен допис отримує 729 переглядів. Протягом першої доби публікація в середньому набирає 375 переглядів.
  • Реакції та взаємодія: Аудиторія активно підтримує контент: середня кількість реакцій на один пост – 7.
  • Тематичні інтереси: Контент зосереджений навколо ключових тем, таких як string, интерфейс, строка, boot, api.

📝 Опис та контентна політика

Автор описує ресурс як майданчик для висловлення суб'єктивної думки:
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Завдяки високій частоті оновлень (останні дані отримано 28 серпня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.

10 115
Підписники
-324 години
-167 днів
-7330 день
Архів дописів
🧠 Осторожно с @Transactional на private-методах! Очень частый анти-паттерн, который легко упустить 👇 @Service public class
🧠 Осторожно с @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

🧠 Коллекторы и toList() в Java 16+: можно ли заменить collect(Collectors.toList()) на просто .toList()? Да, но с нюансами. �
🧠 Коллекторы и 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

VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер! 28–29 июня VK проведёт Weekend Offer для бэкендеров с
VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер! 28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных. Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные! Подробности — на сайте.

🧠 Record (Java 16+) + pattern matching для instanceof (Java 14+) в Java 17+ позволяют писать лаконичный и безопасный код: 📌
🧠 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

🧠 JPA Batch Insert: ускоряем и защищаем от OOM 📌 Настройка Hibernate Добавьте в application.yml или properties: spring: jpa
🧠 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

👩‍💻 JPQL: как писать запросы, которые не сломают Hibernate Узнайте, как писать JPQL-запросы, которые ускорят Hibernate в 5
👩‍💻 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

📌 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

⁉️ Монолит или микросервисы? Руководство для архитекторов, которые ценят свои нервы Приглашаем на открытый урок. 🗓 17 июня в
⁉️ Монолит или микросервисы? Руководство для архитекторов, которые ценят свои нервы Приглашаем на открытый урок. 🗓 17 июня в 20:00 МСК 🆓 Бесплатно. Урок в рамках старта курса «Software Architect». 📌 Что будет на вебинаре: ✔️ Как не попасть в ловушку “модных” микросервисов; ✔️ Разбор признаков, что пора выходить из монолита; ✔️ Архитектурные паттерны для перехода к микросервисам (Strangler Fig, BFF, Self-contained systems); ✔️ Организационные и технические риски — что точно пойдёт не так и как это предсказать; ✔️ Роль DevOps, CI/CD и мониторинга в выборе архитектуры. 👥 Для кого этот вебинар: - Разработчиков Backend и FullStack, участвующих в архитектурных решениях; - Архитекторов ПО, которые планируют масштабирование приложений; - Тимлидов и DevOps-инженеров, выстраивающих процесс разработки и доставки; - Технических менеджеров, выбирающих стратегию развития продукта. 🎯 После вебинара вы: - Получите пошаговое руководство по выбору архитектуры под ваш проект; - Научитесь оценивать реальные риски и стоимость микросервисов; - Поймёте, как внедрять архитектурные изменения без сбоев и хаоса; - Увидите, как принимать взвешенные архитектурные решения, сохраняя технический контроль и производительность команды. 💡 Идеальный вебинар для тех, кто хочет перестать "архитектурить на ощущениях" и начать действовать стратегически. 🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06 👉 Регистрация на вебинар: https://vk.cc/cMHSzz Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

📌 Stream.toList() vs Collectors.toList() — безопасная замена? 🧠 В Java 16+ появился метод Stream.toList(), который собирает
📌 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

⚡️ Быстрые альтернативы HashMap: EnumMap, массивы и примитивные коллекции 🧠 EnumMap вместо HashMap (enum-ключи) 📌 EnumMap х
⚡️ Быстрые альтернативы 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);
⚠️ Работает только для enum-ключей. 🧠 Прямой массив для плотных int-ключей 📌 Используйте массив вместо Map, если ключи — диапазон [0…MAX] и известны заранее.

int MAX = 1000;
var cache = new String[MAX + 1];
cache[42] = "ответ";
String result = cache[42];
💡 O(1), без коллизий и аллокаций объектов. ⚠️ Память линейно зависит от MAX. 🧠 Специализированные коллекции для примитивов 📌 Библиотеки fastutil, HPPC, Trove и др.

var fastMap = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<String>();
fastMap.put(42, "ответ");
String res = fastMap.get(42);
💡 Избегаете boxing/unboxing и получаете быстрее операции. 🧠 Switch-expression / tableswitch 📌 Для фиксированного набора целых или enum-ключей генерируйте switch, а не Map.

String handle(int code) {
    return switch (code) {
        case 100 -> "OK";
        case 200 -> "Created";
        default  -> "Other";
    };
}
💡 JIT компилирует switch в tableswitch или lookupswitch — молниеносно. 💡 Выбирайте стратегию под конкретную задачу: - EnumMap — enum-ключи - Массив — плотные int-диапазоны - fastutil/HPPC — примитивы с большим диапазоном - Switch — фиксированный набор значений 👉@BookJava

+4
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

🚀 Spring WebFlux с Server-Sent Events 🚀 Улучшите свои приложения в режиме реального времени с помощью #SpringWebFlux и Serv
🚀 Spring WebFlux с Server-Sent Events 🚀 Улучшите свои приложения в режиме реального времени с помощью #SpringWebFlux и Server-Sent Events! 🔥 👉@BookJava

📚 Эффективное сжатие текста: код Хаффмана в действии Приглашаем на открытый урок. 🗓 11 июня в 20:00 МСК 🆓 Бесплатно. Урок
📚 Эффективное сжатие текста: код Хаффмана в действии Приглашаем на открытый урок. 🗓 11 июня в 20:00 МСК 🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных». На этом вебинаре мы продолжим разработку архиватора, реализовав код Хаффмана. ✔️ Рассмотрим, как построить дерево кодов, где частота появления символов определяет их битовое представление. ✔️ Интегрируем алгоритм в наш архиватор и проведем сравнительное тестирование с RLE. ✔️ Увидим, как эффективно работает код Хаффмана на текстовых файлах и других типах данных. Отличная возможность изучить продвинутые древовидные структуры данных на практическом примере. Развивайте алгоритмическое мышление, увеличивайте производительность программ. 🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5 👉 Регистрация на вебинар: https://vk.cc/cMzyUv Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

🎯 Java-хаки: динамический вывод с помощью printf() Статья посвящена методу printf() в Java, который используется для создани
🎯 Java-хаки: динамический вывод с помощью printf() Статья посвящена методу printf() в Java, который используется для создания форматированной строки вывода. В ней рассматривается синтаксис метода, различные спецификаторы формата (например, %d, %f, %s и т.д.) и то, как с их помощью управлять отображением чисел, строк и других типов данных в консоли. Приведены примеры использования System.out.printf() с пояснениями по флагам, ширинe и точности форматирования, а также показано, как легко создавать динамический и читаемый вывод в приложениях на Java. https://springframework.guru/java-output-printf-method/ 👉@BookJava

Структурированное логирование в Spring Boot 3.5.5 Spring Boot 3.5.5 приносит улучшенное структурированное логирование. Чтобы
Структурированное логирование в Spring Boot 3.5.5 Spring Boot 3.5.5 приносит улучшенное структурированное логирование. Чтобы его включить, добавьте следующее в ваш application.yml: Это обеспечивает более чистые, структурированные логи, что делает их проще для разбора инструментами вроде ELK, Grafana или Datadog. 👉@BookJava

Как масштабировать машинные модели и работать с огромными объемами данных? Откройте для себя возможности Spark ML на открытом
Как масштабировать машинные модели и работать с огромными объемами данных? Откройте для себя возможности Spark ML на открытом уроке от OTUS! Spark ML — это мощный инструмент для масштабируемого машинного обучения, который позволяет обучать модели на больших данных, не переходя на специализированные ML-системы. Мы покажем, как интеграция с Spark SQL и DataFrame API упрощает ETL-подготовку данных и фичуризацию для реальных проектов. Убедитесь, как Spark ML решает задачи отказоустойчивости и распределённых вычислений, позволяя вам легко строить промышленные ML-пайплайны. Посетите открытый урок 11 июня в 20:00 МСК в преддверие старта курса «Spark Developer» и получите скидку на обучение: https://vk.cc/cMyLJ3 Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

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);
    }
}
⚠️ Помните: если пул заполнится, новые задачи будут либо ждать (до исчерпания queueCapacity), либо бросать RejectionException. Настройте RejectedExecutionHandler по необходимости. 📌 Способ 2: Resilience4j Bulkhead (Semaphore vs ThreadPool)

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() { … }
или ThreadPool-вариант:

resilience4j.bulkhead.instances:
  myThreadPoolBulkhead:
    maxThreadPoolSize: 10
    queueCapacity: 20

@Bulkhead(name = "myThreadPoolBulkhead", type = Bulkhead.Type.THREADPOOL)
public CompletionStage<String> asyncProcess() { … }
🧠 Неочевидный момент про паттерны в целом: внедрять Bulkhead “просто потому что модно” — плохо. Паттерн не заменяет мониторинг, трассировку или грамотную архитектуру. Он лишь ограничивает повреждения, но не показывает, где именно проблема. Если вы изолировали компонент в пул, а он всё равно падает, паттерн не скажет “почему”. Всегда сочетайте паттерны с метриками (Micrometer, Prometheus, Grafana) и логированием. 💡 Совет: ▫️Используйте отдельные пулы для медленных операций (например, внешних HTTP-вызовов) и отдельно для CPU-bound задач. ▫️На уровне базы данных тоже можно “бульхедом” выделять разные пулы соединений (например, HikariCP с разными конфигурациями) для тяжелых и легких запросов. ▫️При проектировании микросервисов отдавайте предпочтение Bulkhead на уровне отдельных сервисов: в Kubernetes это можно делать через limits/requests, Horizontal Pod Autoscaling и Circuit Breaker. ⚠️ Предупреждение: перебор с изоляцией приведет к недоиспользованию ресурсов. Если у вас слишком много мелких пулов, а нагрузка неравномерна, часть ресурсов простаивает. Поэтому сначала измерьте нагрузку, а потом разбивайте. 👉@BookJava

👩‍💻 Java — один из самых востребованных языков, но не каждый разработчик умеет использовать его возможности по максимуму. Н
👩‍💻 Java — один из самых востребованных языков, но не каждый разработчик умеет использовать его возможности по максимуму. На курсе «Java Developer. Professional» вы научитесь создавать современные Java-приложения, освоите Spring WebFlux и Kafka, а также разберётесь в работе JVM изнутри. Пройдите тест, проверьте, достаточно ли у вас знаний для обучения на курсе:. 🎁 Дарим промокод, который дает скидку на обучение - JAVA_06 На курсе вас ждёт практическая работа с кодом, детальные разборы, ревью от экспертов и подходы, позволяющие писать эффективный и чистый код. Начните свой путь к уровню Middle+ и используйте Java на 100%. ➡️ Пройти вступительный тест курса: https://vk.cc/cMxZwr Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

30 лет Java: от провалившегося гаджета до фундамента разработки ПО Некоторые языки программирования, например Rust, Go и Type
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

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() поможет аккуратно пропустить «пустышки» и не ломать последующие операции.
⚠️ Но вот в чём «подводные камни» и почему нельзя злоупотреблять: 1. Потеря явности ◾️ Когда вы где-то просто хотите проверить: есть ли значение в Optional, — использование stream() создаёт впечатление, что у вас реально коллекция элементов, хотя всего лишь 1 или 0. Для простых случаев ifPresent(), map(), orElse() читается понятнее.

   // Менее канонично:
   optionalValue.stream().forEach(v -> doSomething(v));
   // Лучше:
   optionalValue.ifPresent(v -> doSomething(v));
   
2. Ненужные накладные расходы ◾️ Каждый вызов Optional.stream() создаёт объект стрима и небольшую внутреннюю структуру, что на горячем участке кода (в tight loop) может сказаться на производительности. Если вместо него можно обойтись map().orElse(), задумайтесь о легковесном варианте. 3. Скрытые баги ◾️ Если вы по ошибке используете Optional.stream() в одиночном случае (не в контексте объединения множества опционалов), код может стать менее очевидным. Например:

   // Что тут происходит?
   Stream.of(opt1, opt2, opt3)
         .flatMap(Optional::stream)
         .findFirst();
   
Казалось бы, надо искать первый непустой, но читающий код может не сразу понять логику: а вдруг нужно просто взять любое значение, а не первой в списке? Лушче явно:

   Optional<User> result = opt1.isPresent() ? opt1
       : opt2.isPresent() ? opt2
       : opt3;
   
4. Лишняя сложность ◾️ В ситуациях, когда Optional появляется из map/filter в одном стриме, а потом вы вновь оборачиваете результат в Optional, лучше сразу строить последовательность через 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 и пропустить пустые. ◾️ ⚠️ В одиночных сценариях проверки и извлечения значения он избыточен и даже снижает читабельность и производительность. 👉@BookJava