ch
Feedback
Java Geek

Java Geek

前往频道在 Telegram

Практичные советы, лайфхаки и код для Java-разработчиков. Каждый пост — реальная польза. Учим Java на примерах. По всем вопросам @evgenycarter

显示更多
2 449
订阅者
无数据24 小时
+27
+530
帖子存档
Fluent Interface Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийс
Fluent Interface Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийся как текст на естественном языке. Он достигается путем создания методов, которые возвращают ссылку на this объект, позволяя вызывать их в цепочке. 👉 @java_geek

🧠 Почему @Transactional не работает? Один из самых частых вопросов: "Я поставил @Transactional, но транзакция не откатывается. Почему?" 📌 Ответ — в механизме прокси Spring. Spring оборачивает бины с @Transactional в прокси, которые перехватывают вызовы и управляют транзакцией. Но работает это только при вызове метода извне. Если ты вызываешь метод с @Transactional внутри того же класса, прокси не используется, и аннотация игнорируется. 💡 Пример:

@Service
public class UserService {
    public void registerUser() {
        createUser(); // ❌ Транзакция не работает!
    }

    @Transactional
    public void createUser() {
        // изменения в БД
    }
}
📎 Решения: 1. Вынести метод в другой бин:

@Service
public class UserService {
    private final UserWriter writer;

    public UserService(UserWriter writer) {
        this.writer = writer;
    }

    public void registerUser() {
        writer.createUser(); // ✅ работает
    }
}

@Service
public class UserWriter {
    @Transactional
    public void createUser() {
        // изменения в БД
    }
}
2. Или вызвать себя через ApplicationContext:

@Autowired
private ApplicationContext context;

public void registerUser() {
    context.getBean(UserService.class).createUser(); // ✅ работает
}
⚠️ Но лучше использовать первый способ — он чище архитектурно. 👉 @java_geek

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Go📌 https://max.ru/golang_lib Библиотека Go (Golang) разработчика Программирование React📌 https://max.ru/react_lib React Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

🗑️ Java Garbage Collector: Кто убирает за вами мусор? Разработчики на C и C++ живут в постоянном страхе утечек памяти: выделил память через malloc - обязан очистить через free. Мы же в Java просто пишем new Object() и идем пить кофе. Всю грязную работу делает Garbage Collector (Сборщик мусора или просто GC). Но если не понимать, как он работает, ваше приложение однажды просто "зависнет" на пару секунд на продакшене. 🛑 Главная проблема: Stop-The-World GC не может убирать мусор, пока ваше приложение активно меняет ссылки на объекты. Ему нужно поставить всё на паузу. Эта пауза называется Stop-The-World (STW). В этот момент все ваши потоки замирают, пользователи видят "колесико загрузки", а запросы по сети отваливаются по таймауту. Вся эволюция GC в Java - это борьба за уменьшение этих пауз. 🧬 Гипотеза поколений Как GC понимает, что удалять? Он опирается на одно гениальное наблюдение: 98% объектов умирают молодыми. (Например, локальные переменные внутри метода живут доли секунды). Поэтому память (Heap) поделили на две части: 1. Young Generation (Молодое поколение): Сюда попадают все новые объекты. Очистка здесь происходит часто и невероятно быстро (Minor GC). 2. Old Generation (Старое поколение): Сюда "переезжают" объекты-долгожители (например, закэшированные данные или синглтоны Spring). Очистка здесь происходит редко, но занимает много времени (Major/Full GC). 🥊 Битва титанов: Какой GC выбрать? В современных версиях Java вам, как правило, нужно знать о двух главных сборщиках. 1. G1 (Garbage-First)Статус: Включен по умолчанию с Java 9. • Как работает: Дробит память на сотни мелких регионов. Во время уборки он смотрит: "Ага, вот в этом регионе 90% мусора, начну с него" (отсюда и название - мусор в первую очередь). • Кому подходит: 95% обычных веб-приложений. Он отлично балансирует между высокой пропускной способностью и приемлемыми паузами (целевая пауза по умолчанию — 200 мс). 2. ZGC (Z Garbage Collector)Статус: Готов к бою (Production Ready) с Java 15, а в Java 21 стал генерационным. • Как работает: Настоящая магия и инженерное чудо. ZGC выполняет почти всю работу параллельно с вашим приложением, используя "цветные указатели" (colored pointers). • Суперсила: Паузы Stop-The-World не превышают 1 миллисекунды, даже если у вас куча (Heap) размером в 16 Терабайт! • Кому подходит: Финансовым биржам, игровым серверам и системам, где важна ультра-низкая задержка (Low Latency). 🛠 Как включить? Ничего устанавливать не нужно, просто добавьте флаг при запуске java -jar: • Для G1 (если у вас старая Java): -XX:+UseG1GC • Для ZGC: -XX:+UseZGC 🧠 Золотое правило Memory Management Сборщик мусора в Java невероятно умен. Не пытайтесь ему "помогать". Вызовы System.gc() в коде - это выстрел себе в ногу. Просто пишите чистый код, не держите ссылки на объекты, которые вам больше не нужны, и GC сделает всё сам. #Java #GarbageCollector #Performance #JVM #Backend 👉 @java_geek

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

⚖️ System Design: Балансировщик нагрузки (Load Balancer). Как не уронить сервера? В прошлом посте мы поняли, что один сервер не справляется, и запустили еще три таких же. Но как пользователи узнают, к какому из них подключаться? Не выдавать же им три разных IP-адреса! Здесь на сцену выходит Load Balancer (LB) - регулировщик вашего трафика. LB становится единственной точкой входа. Он принимает на себя все запросы от пользователей и по-умному раскидывает их по вашим серверам. Но как именно он решает, куда отправить следующий запрос? Для этого есть алгоритмы. 🧠 Главные алгоритмы балансировки 1. Round Robin (Карусель) Самый простой и популярный по умолчанию. Запросы раздаются по кругу: первому серверу, второму, третьему, снова первому. • Плюсы: Легко настроить, нулевая нагрузка на сам LB. • Минусы: Слепой алгоритм. Если 1-й сервер завис, генерируя тяжелый отчет, а 2-й свободен, LB всё равно кинет им запросы поровну. 1-й сервер умрет окончательно. 2. Least Connections (Кто свободнее?) LB работает как умный менеджер: он считает, сколько активных соединений висит на каждом сервере прямо сейчас. Новый запрос летит туда, где меньше всего работы. • Идеально для: Приложений с долгими соединениями (чаты на WebSockets, потоковая передача видео, скачивание файлов). 3. IP Hash (Липкие сессии / Sticky Sessions) LB берет IP-адрес пользователя, прогоняет через хэш-функцию и привязывает этот IP к конкретному серверу. • Зачем нужно: Если ваше (легаси) приложение хранит корзину товаров в оперативной памяти конкретного сервера, вам критически важно, чтобы юзер всегда попадал на один и тот же сервер. Иначе на следующем клике его корзина "опустеет". • Современный совет: Старайтесь избегать Sticky Sessions. Храните сессии в Redis, чтобы любой сервер мог обработать любой запрос. 4. Weighted алгоритмы (Система весов) У вас в кластере два сервера: новый 32-ядерный монстр и старенькая 4-ядерная виртуалка. Если включить обычный Round Robin, старый сервер сгорит. Вы задаете им "веса" (например, 8 и 1). Теперь мощный сервер будет получать 8 запросов на каждый 1 запрос к слабому. 🛠 Суперспособности балансировщиков LB - это не только про алгоритмы. У него есть еще две критически важные функции: • 🩺 Health Checks (Проверка пульса): Балансировщик постоянно "пингует" свои сервера (например, запрашивает /health). Если сервер не ответил 3 раза подряд, LB помечает его как "мертвый" и перестает слать на него трафик. Пользователи даже не заметят, что один из серверов сгорел. • 🔒 SSL Termination: Расшифровка HTTPS-трафика отнимает много ресурсов процессора. Балансировщик может взять эту тяжелую криптографию на себя. Он расшифровывает запрос, а дальше внутри вашей приватной (безопасной) сети общается с серверами по быстрому и легкому HTTP. #SystemDesign #Backend #LoadBalancer #Architecture #DevOps 👉 @java_geek

В чем отличия между TreeSet и HashSet? Основное отличие между TreeSet и HashSet заключается в том, что TreeSet хранит элемент
В чем отличия между TreeSet и HashSet? Основное отличие между TreeSet и HashSet заключается в том, что TreeSet хранит элементы в отсортированном порядке, в то время как порядок хранения элементов в HashSet не определен. TreeSet также обеспечивает быстрый поиск и извлечение элементов в отсортированном порядке. 👉 @java_geek

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

🏗 System Design: Эволюция архитектуры от 1 до 1 000 000 пользователей Главная ошибка разработчиков при проектировании систем - строить звездолет для поездки за хлебом. Микросервисы, Kafka и Kubernetes не нужны вашему стартапу в первый день. Архитектура должна эволюционировать шаг за шагом. Вот как выглядит этот путь. Уровень 1: Одинокий Волк (1 - 1000 юзеров) Всё крутится на одном сервере (например, в DigitalOcean или AWS EC2). • Что там: Ваше Java-приложение (Monolith) + база данных (PostgreSQL) + веб-сервер (Nginx) живут на одной машине. • Плюсы: Развертывание занимает 5 минут, всё работает быстро (сетевые задержки нулевые). • Минусы: Если сервер упал - упало всё. Масштабировать можно только покупкой более мощного процессора/памяти (Вертикальное масштабирование). Уровень 2: Разделение труда (10 000 юзеров) Приложение начинает тормозить, потому что СУБД "съела" всю оперативную память. • Что делаем: Выносим базу данных на отдельный сервер. Желательно использовать управляемое решение (Managed DB от облачного провайдера), чтобы не возиться с бэкапами. • Результат: Приложение и БД больше не дерутся за ресурсы. Уровень 3: Горизонтальное масштабирование (100 000 юзеров) Трафик растет. Один сервер приложения больше не справляется с HTTP-запросами. • Что делаем: Ставим Load Balancer (Балансировщик нагрузки) и поднимаем 3-5 одинаковых серверов с вашим Java-приложением. • Правило: Ваше приложение должно стать Stateless (без состояния). Вы больше не можете хранить сессии пользователей в локальной памяти (RAM), иначе юзер залогинится на Сервере 1, а следующий запрос попадет на Сервер 2, и его "выкинет". Сессии уезжают в централизованное хранилище. Уровень 4: Спасаем базу данных (500 000 юзеров) Приложений много, а БД одна. Она начинает "задыхаться" от количества чтений. • Что делаем (Кэш): Ставим Redis или Memcached. До 80% запросов в типичном приложении - это чтение одних и тех же данных. Кэш отдает их за миллисекунды. • Что делаем (Репликация): Разделяем БД на Master (для записи) и несколько Slave/Replica (только для чтения). Уровень 5: Асинхронность и Очереди (1 000 000+ юзеров) Пользователи жалуются, что загрузка отчета или обработка видео занимает слишком много времени, а HTTP-соединения отваливаются по таймауту. • Что делаем: Внедряем брокер сообщений (Kafka или RabbitMQ) и создаем воркеры. • Как это работает: Юзер жмет "сгенерировать отчет". Приложение кидает задачу в Kafka и мгновенно отвечает юзеру: "В процессе". А фоновые серверы-воркеры не спеша забирают задачи из очереди и делают тяжелую работу. 🧠 Главный принцип System Design Не усложняйте систему до тех пор, пока метрики не покажут, что текущий уровень больше не справляется. Каждое усложнение (Load Balancer, Redis, Kafka) несет за собой новые проблемы: инвалидация кэша, задержки сети, дублирование сообщений. #SystemDesign #Architecture #Backend #Scaling 👉 @java_geek

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

👩‍💻 Открытый урок «Изучаем Java с нуля на примере простой консольной игры» 🗓 2 марта в 20:00 МСК 🆓 Бесплатно. Урок в рамк
👩‍💻 Открытый урок «Изучаем Java с нуля на примере простой консольной игры» 🗓 2 марта в 20:00 МСК 🆓 Бесплатно. Урок в рамках старта курса «Java-разработчик» от Otus. На вебинаре вы узнаете как установить и настроить инструменты, необходимые для программирования на языке Java. Научитесь создавать небольшие консольные проекты. Изучите несколько базовых блоков из которых строится любое приложение. Познакомитесь с процессом разработки простой консольной игры. О чем поговорим: ✔ Как установить и настроить инструменты, необходимые для программирования на языке Java ✔ Как создавать небольшие консольные проекты ✔ Несколько базовых блоков, из которых строится любое приложение ✔ Процесс разработки простой консольной игры Кому будет интересно: Урок будет полезен тем, кто хочет: — Начать изучать программирование на языке Java с нуля. — Посмотреть, как на практике создаются Java-приложения. 🔗 Ссылка на регистрацию: https://vk.cc/cUPIcw Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

🌉 Project Panama. Как вызвать C/C++ без боли? Java долгое время жила в изоляции. Чтобы выйти за пределы JVM (вызвать функцию из OS, использовать библиотеку ML на C++ или графику на Rust), нам приходилось писать "клей" (glue code) на C. Project Panama (Foreign Function & Memory API) меняет парадигму. Теперь Java умеет говорить с нативным кодом напрямую. 🗑 Что мы выбрасываем (JNI) 1. Больше не нужно писать ни строчки на C/C++. 2. Больше нет утилиты javah. 3. Больше нет риска, что Unsafe удалят и ваш код сломается. ✨ Как это работает (FFM API) Допустим, мы хотим вызвать стандартную функцию strlen из библиотеки C, чтобы узнать длину строки. Шаг 1. Находим библиотеку

Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();

// Ищем адрес функции "strlen" в памяти процесса
MemorySegment strlenAddress = stdlib.find("strlen").orElseThrow();

Шаг 2. Описываем сигнатуру (Descriptor) Мы говорим Java: "Эта функция принимает указатель (Адрес) и возвращает число (long)".

FunctionDescriptor descriptor = FunctionDescriptor.of(
    ValueLayout.JAVA_LONG,    // Возвращаемое значение
    ValueLayout.ADDRESS       // Аргумент (указатель на строку)
);

MethodHandle strlen = linker.downcallHandle(strlenAddress, descriptor);

Шаг 3. Выделяем память и вызываем Самое интересное: мы аллоцируем память вне Java кучи (Off-heap) безопасным способом через Arena.

try (Arena arena = Arena.ofConfined()) {
    // Превращаем Java String в C-строку (char*)
    MemorySegment cString = arena.allocateFrom("Hello Panama!");

    // Вызываем функцию C прямо из Java!
    long length = (long) strlen.invoke(cString);
    
    System.out.println(length); // 13
} 
// Тут память автоматически очищается (как free() в C)

🤖 Killer Feature: jextract Вы скажете: "Писать дескрипторы для каждой функции вручную? Это же муторно!" И вы правы. Поэтому есть утилита jextract. Вы просто "скармливаете" ей заголовочный файл .h:

jextract --output src -t org.example.opengl /usr/include/GL/gl.h

Она сама сгенерирует все Java-классы и методы. Вы просто пишете:

gl.glClear(gl.GL_COLOR_BUFFER_BIT());

Это выглядит и работает как обычный Java-код, но под капотом вызывает OpenGL напрямую. 🧠 Зачем это нужно? 1. AI и ML: TensorFlow, PyTorch, OpenCV - все они написаны на C++. Теперь Java может использовать их без тормозов JNI. 2. Базы данных: Драйверы могут работать напрямую с сетевым стеком OS (io_uring). 3. Rust: Вы можете написать критически важную логику на Rust, скомпилировать в библиотеку и использовать в Java. 🏆 Итог серии Modern Java • Она запускается мгновенно (GraalVM). • Она держит миллионы соединений (Virtual Threads). • Она безопасна и лаконична (Records, Sealed Classes). • Она открыта миру (Panama). #ProjectPanama #FFM #Native #Performance 👉 @java_geek

🏗 Java: Структурная конкурентность. Прощайте, зомби-потоки! Допустим, вы используете Виртуальные потоки (Project Loom), чтобы сделать два независимых запроса: получить данные пользователя (API 1) и его заказы (API 2). Раньше мы использовали ExecutorService или CompletableFuture. Но у них есть огромная архитектурная дыра: они ничего не знают друг о друге. 🧟‍♂️ Проблема зомби-потоков (Unstructured Concurrency) Если API 1 мгновенно падает с ошибкой 500, ваш метод все равно будет ждать, пока API 2 доработает (или упадет по таймауту). Поток, качающий заказы, становится "сиротой". Он делает бесполезную работу, тратит сеть и память, хотя результат уже никому не нужен. А если ошибку выкинет родительский метод? Дочерние потоки продолжат жить своей жизнью в фоне. Это хаос. 🧩 Решение: StructuredTaskScope В современной Java (начиная с 21 версии) потоки привязали к лексической области видимости - блоку кода. Если мы выходим из блока (из-за ошибки или успешного завершения), все запущенные внутри него дочерние потоки автоматически отменяются (получают interrupt). Вот как выглядит "Запрос-Ответ", где должны выполниться оба действия (Стратегия *All or Nothing*):

// ShutdownOnFailure: если один упал, отменяем остальные
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    
    // Запускаем подзадачи (Subtasks)
    Subtask<User> user = scope.fork(() -> fetchUser(id));
    Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));

    scope.join();          // Ждем завершения обеих задач...
    scope.throwIfFailed(); // ...или первой же ошибки!

    // Сюда дойдем, только если обе задачи успешны
    return new UserProfile(user.get(), orders.get());
} 
// При выходе из блока любые зависшие потоки будут убиты

🏎 Стратегия "Кто первый, тот и прав" А что, если вам нужно получить курс валют, и у вас есть 3 разных провайдера? Вам нужен ответ от любого, кто ответит быстрее.

// ShutdownOnSuccess: первый успешный отменяет остальные
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Quote>()) {
    
    scope.fork(() -> getFromBankA());
    scope.fork(() -> getFromBankB());
    scope.fork(() -> getFromBankC());

    scope.join(); // Ждем первого успешного

    return scope.result(); // Возвращаем самый быстрый ответ
}

Как только Банк А ответит, запросы к Банкам B и C будут немедленно отменены. Никакого ручного управления Future.cancel(). Всё работает из коробки. 🧠 Почему это меняет всё? 1. Читаемость: Многопоточный код читается сверху вниз, как обычный синхронный. 2. Безопасность ресурсов: Утечки потоков физически невозможны. Структура гарантирует, что родитель не завершится, пока не разберется со всеми детьми. 3. Идеальные логи: Стек-трейс теперь показывает реальную иерархию (кто кого вызвал), а не обрывается на внутренностях пула потоков. В связке с Виртуальными потоками это делает Java одним из самых удобных языков для написания высоконагруженных сетевых приложений. #Concurrency #ProjectLoom #CleanCode #Backend 👉 @java_geek

🚀 Java на стероидах: Запуск за 0.05 сек с GraalVM Native Image Главный аргумент хейтеров Java: "Она жирная и медленная на старте". И они правы. JVM нужно время, чтобы прогреться, загрузить классы и скомпилировать байт-код (JIT). Для микросервисов в Kubernetes или Serverless (AWS Lambda) это критично. Но GraalVM Native Image превращает ваше Java-приложение в нативный бинарник. Без JVM. Без java -jar. Просто исполняемый файл. 🏎 JIT vs AOT: В чем разница? 1. Standard JVM (JIT - Just In Time): Вы запускаете код -> JVM интерпретирует его -> находит "горячие участки" -> компилирует их в машинный код прямо во время работы. • Плюс: Максимальная производительность на длинной дистанции (Runtime Optimization). • Минус: Медленный старт, жрет память на компилятор. 2. Native Image (AOT - Ahead Of Time): Вы компилируете код заранее. Весь "мертвый" код выкидывается. Остается только то, что реально используется. • Плюс: Мгновенный старт (< 100ms), мизерное потребление памяти. • Минус: Долгая сборка (build time), нет оптимизаций во время выполнения. 🛠 Как это выглядит на практике? В Spring Boot 3 поддержка Native Image встроена официально. 1. Добавляем плагин (Gradle):

plugins {
    id("org.springframework.boot") version "3.2.0"
    id("org.graalvm.buildtools.native") version "0.9.28"
}

2. Собираем:

./gradlew nativeCompile

Внимание: Идите пить кофе. Сборка займет от 2 до 10 минут. GraalVM проводит статический анализ всего вашего кода и всех библиотек. 3. Запускаем:

./build/native/nativeCompile/my-app
> Started MyApp in 0.048 seconds (JVM running for 0.052)

Да, вы не ослышались. Spring Boot поднимается за 50 миллисекунд. ⚠️ Подводные камни (The Pain Points) Не всё так радужно. Native Image ненавидит динамику. 1. Reflection API: GraalVM должен знать обо всех классах, которые вы загружаете через рефлексию, на этапе сборки. Раньше нужно было писать тонны JSON-конфигов. Сейчас Spring Boot делает это за вас, но сторонние либы могут сломаться. 2. Resources: Если вы читаете файл из src/main/resources не стандартным способом, его может не оказаться в бинарнике. 3. Нет "Write Once, Run Anywhere": Бинарник под Linux не запустится на Windows. Сборку нужно делать в той же OS (или в Docker-контейнере), где будет работать код. 📊 Когда использовать?Serverless (AWS Lambda, Google Cloud Run): Холодный старт больше не проблема. ✅ CLI утилиты: Консольные тулзы на Java теперь работают мгновенно. ✅ Kubernetes (Scale to Zero): Поды поднимаются и умирают за секунды, экономя ресурсы. ⛔ Обычные микросервисы (High Load): Если сервис живет месяцами и молотит тысячи запросов в секунду, обычная JVM (JIT) обгонит Native Image по пропускной способности (Throughput) за счет рантайм-оптимизаций. #GraalVM #NativeImage #JavaPerformance #SpringBoot3 #Serverless 👉 @java_geek

🐳 Java Testing: Хватит мокать базы данных! Вы всё еще используете H2 для тестов, а в продакшене стоит PostgreSQL? Или, может быть, вы поднимаете docker-compose руками перед запуском mvn test? ⛔ Как было раньше Раньше нам приходилось делать трюки с @DynamicPropertySource, чтобы вручную прокинуть jdbc:url, username и password из поднятого контейнера в контекст Spring.

// Old School (Java 17 / Boot 2.7)
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", postgres::getJdbcUrl);
    registry.add("spring.datasource.username", postgres::getUsername);
    // ... куча ручного кода
}

✅ Как сейчас @ServiceConnection сама понимает, что это за контейнер (Postgres, Redis, Kafka, RabbitMQ), и автоматически прописывает нужные свойства в ваш application.properties на время теста.

@Testcontainers
@SpringBootTest
class OrderIntegrationTest {

    // 1. Объявляем контейнер
    @Container
    @ServiceConnection // 👈 Вся магия тут!
    static PostgreSQLContainer<?> postgres = 
        new PostgreSQLContainer<>("postgres:16-alpine");

    @Autowired
    private OrderRepository repository;

    @Test
    void shouldSaveOrder() {
        // 2. Тест работает с РЕАЛЬНОЙ базой в Docker
        var order = new Order("item-123", BigDecimal.TEN);
        repository.save(order);
        
        assertThat(repository.findAll()).hasSize(1);
    }
}

Писать контейнер в каждом тестовом классе - дурной тон (и медленно, так как Docker будет стартовать каждый раз заново). В Spring Boot 3.1 появился паттерн TestConfiguration, который позволяет переиспользовать контейнеры. Создаем один файл конфигурации:

@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {

    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:16-alpine");
    }

    @Bean
    @ServiceConnection
    public KafkaContainer kafkaContainer() {
        return new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"));
    }
}

Теперь в любом тесте просто импортируем этот конфиг:

@Import(ContainersConfig.class)
@SpringBootTest
class AnyIntegrationTest { ... }

Вы можете использовать этот же конфиг, чтобы запустить само приложение локально с поднятыми контейнерами! Больше не нужно настраивать локальный Postgres. В папке src/test/java создайте класс для запуска:

public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.from(MyRealApplication::main)
            .with(ContainersConfig.class) // Подключаем наши контейнеры
            .run(args);
    }
}

Запускаете TestApplication -> Spring сам поднимает Docker-контейнеры -> приложение коннектится к ним -> вы работаете. 🏁 ИтогИзоляция: Каждый прогон тестов - чистая среда. • Честность: Тестируйте на том, что будет в продакшене. • Удобство: @ServiceConnection убрал весь бойлерплейт. #SpringTest #Testcontainers #Java #DevOps #Docker 👉 @java_geek

🔴 Завтра тестовое собеседование с Java-разработчиком 11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собес
🔴 Завтра тестовое собеседование с Java-разработчиком 11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика. Как это будет: 📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу 📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью 📂 В конце можно будет задать любой вопрос Сергею Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы. Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot Реклама. О рекламодателе.

🔒 Java Sealed Classes: Диктатура в вашей иерархии Раньше в Java у нас было всего два стула для классов: 1. Public: Наследуйся кто хочет (открытый проходной двор). 2. Final: Никто не пройдет (полная изоляция). Но что, если я хочу разрешить наследование только моим классам Circle и Square, но запретить Васе из соседнего отдела создавать свой кривой Triangle? Начиная с Java 17, у нас есть Sealed Classes. 🚧 Как это работает? Вы используете ключевое слово sealed и permits, чтобы явно перечислить, кому дозволено быть вашим наследником.

public sealed interface PaymentResult 
    permits Success, Failure, Pending {
}

Теперь компилятор гарантирует: в мире существуют только три вида PaymentResult. Четвертого не дано. 🤝 Идеальная пара: Sealed + Records Чаще всего наследниками делают record, потому что они идеально подходят для хранения данных.

public record Success(String txId) implements PaymentResult {}
public record Failure(String error) implements PaymentResult {}
public record Pending(long timestamp) implements PaymentResult {}

🧠 Главная фишка: Умный Switch (Pattern Matching) Зачем нам эти ограничения? Ради исчерпываемости (Exhaustiveness). Когда вы используете sealed классы в новых switch (Java 21+), компилятор знает все возможные варианты. Вам не нужно писать default ветку!

String message = switch (result) {
    case Success s -> "Paid! ID: " + s.txId();
    case Failure f -> "Error: " + f.error();
    case Pending p -> "Wait...";
    // Нет default! И это безопасно.
};

В чем магия? Если через полгода вы добавите в permits новый вариант Cancelled, ваш код перестанет компилироваться везде, где используется этот switch. Компилятор ткнет вас носом: "Ты забыл обработать новый статус!". Это спасает от сотен багов в сложной бизнес-логике. 📜 Три правила для наследников Наследник sealed класса обязан выбрать одну из трех стратегий: 1. final: На мне иерархия заканчивается (как в Records). 2. sealed: Я продолжаю жесткий контроль, вот мои наследники. 3. non-sealed: Я открываю шлюзы - от меня может наследоваться кто угодно (возврат к старому поведению). 🚀 Итог: Используйте Sealed Classes, когда ваша модель данных представляет собой конечное множество вариантов: 🔴Статусы заказа 🔴Типы пользователей (Admin, User, Guest) 🔴Результаты операций (Success, Error) Это делает код предсказуемым и безопасным на уровне компилятора. #Java17 #Architecture #CleanCode #PatternMatching 👉 @java_geek

💿 Java Records: Конец эпохи Lombok? Долгие годы Project Lombok был нашим единственным спасением от бесконечных геттеров, сеттеров, equals() и hashCode(). Но начиная с Java 14 (и официально в Java 16), в языке появилась нативная альтернатива - Records. Многие поспешили удалить Lombok и переписать всё на рекорды. И... столкнулись с проблемами. Давайте разберем, почему record - это не просто "короткий класс". 🏎 Скорость написания Lombok (@Value для неизменяемости):

@Value
public class User {
    String name;
    int age;
}

Record:

public record User(String name, int age) {}

Тут победа за рекордами. Синтаксис максимально лаконичен. Мы объявляем состояние, а не поля. 🔍 Что под капотом? (Главные отличия) Вот здесь начинаются нюансы, из-за которых Records нельзя назвать полной заменой Lombok. 1. Жесткая неизменяемость (Immutability) Record - это всегда final класс с final полями. Вы не можете сделать "сеттер" в рекорде. Если вам нужен изменяемый DTO (например, для заполнения формы по шагам) - Record не подойдет. Lombok @Data всё еще нужен. 2. Запрет на наследование Рекорды не могут наследоваться от других классов (они уже неявно наследуются от java.lang.Record). • Если у вас есть BaseEntity с полем ID - вы не сможете унаследовать рекорд от него. 3. Имена геттеров Это ломает многие старые библиотеки. • Lombok/JavaBean: getName(), getAge() • Record: name(), age()Совет: Jackson и современные JSON-библиотеки уже умеют с этим работать, но легаси-фреймворки могут "не увидеть" ваши поля. ✨ Killer Feature: Компактные конструкторы То, чего нет в Lombok. Валидация данных в рекордах выглядит потрясающе чисто. Вам не нужно дублировать аргументы:

public record User(String name, int age) {
    // Обратите внимание: нет скобок с аргументами!
    public User {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
        name = name.toUpperCase(); // Можно даже менять данные перед записью
    }
}

🏆 Вердикт: Кто победил? Никто. Это инструменты для разных целей. ✅ Используйте record: Для DTO, ключей в Map, ответов API, записей в Event Log и промежуточных данных в Stream API. Это "именованные кортежи" данных. ✅ Используйте Lombok / POJO: Для JPA сущностей (Hibernate не очень любит рекорды из-за прокси и отсутствия сеттеров), для классов с наследованием и там, где нужна мутабельность. #Java #Lombok #CleanCode #Java17 👉 @java_geek

🕵️ Java var: Удобный сахар или скрытая угроза? С выходом Java 10 ключевое слово var позволило нам писать меньше кода. var - это Local Variable Type Inference. Это значит, что Java осталась строго типизированным языком, просто теперь компилятор сам догадывается о типе переменной, глядя на то, что находится справа от знака равно (=). Но иногда его догадки могут вас удивить. Вот 3 примера, где var работает неочевидно. 1. Ловушка "Diamond Operator" (<>) Самая частая ошибка новичков.

// Без var (Классика)
List<String> list = new ArrayList<>(); 
// Компилятор видит слева List<String> и понимает, что справа тоже String.

// С var (Ошибка)
var list = new ArrayList<>(); 

В чем проблема? У компилятора нет информации о типе. Он видит пустые скобки <> и решает, что это ArrayList<Object>. В итоге вы теряете типизацию:

list.add("Hello");
list.add(123); // ✅ Это сработает, хотя вы, вероятно, хотели только строки!

✅ Как исправить: Если используете var с конструктором, всегда указывайте тип справа:

var list = new ArrayList<String>();

2. Магия "Пересечения типов" (Intersection Types) А вот это уже высший пилотаж, который часто встречается при использовании List.of() или Map.of(). Что будет, если сложить в список данные разных типов?

var magicList = List.of(10, 20.5, "30"); 
// Какой тут тип списка? List<Object>?

На самом деле компилятор выведет наиболее специфичный общий тип. Тип переменной magicList будет выглядеть примерно так: List<? extends Serializable & Comparable<? extends Serializable & Comparable<?>>> Java находит общие интерфейсы для Integer, Double и String (все они реализуют Serializable и Comparable) и создает этот ужасный тип-франкенштейн. Это работает, но может свести с ума вашу IDE или методы, принимающие конкретные типы. 3. Анонимные классы на стероидах var позволяет делать трюк, невозможный ранее: сохранять тип анонимного класса.

var user = new Object() {
    String name = "Alex";
    int age = 25;
};

// Это работает!
System.out.println(user.name); 
System.out.println(user.age);

Если бы мы написали Object user = ..., поля name и age были бы недоступны. А var "видит" реальную структуру анонимного объекта. Это иногда полезно для локальных промежуточных вычислений, заменяя DTO или кортежи. 🧠 Золотое правило использования var хорош тогда, когда тип очевиден из правой части: 🔴👍 var users = Map.of("id", 1); (Понятно, что это Map) 🔴👍 var stream = list.stream(); (Понятно, что Stream) 🔴👎 var result = service.process(); (Что вернулось? boolean? User? null?) Читаемость кода важнее краткости! #Java #CleanCode #Var #Java10 👉 @java_geek

⚔️ Java Battle: List.of() vs Arrays.asList() - в чем разница? На код-ревью часто можно увидеть, как эти два метода используют взаимозаменяемо. На первый взгляд, результат один - список элементов. Но под капотом это два совершенно разных зверя. Давайте разберем, почему замена одного на другой может сломать ваш код. 1. Иллюзия неизменяемости (The Mutability Trap) Это самое важное различие. 🔴List.of() (Java 9+): Создает по-настоящему неизменяемый список. Вы не можете ни добавить, ни удалить, ни изменить элемент. 🔴Arrays.asList() (Java 1.2+): Создает список фиксированного размера, который "обернут" вокруг массива. Вы не можете менять размер (add/remove), но можете заменять элементы!

var legacyList = Arrays.asList("A", "B");
var modernList = List.of("A", "B");

legacyList.set(0, "C"); // ✅ РАБОТАЕТ! Список теперь ["C", "B"]
modernList.set(0, "C"); // ❌ Ошибка: UnsupportedOperationException

legacyList.add("D");    // ❌ Ошибка (размер фиксирован)
modernList.add("D");    // ❌ Ошибка (полная иммутабельность)

2. Отношение к null 🔴Arrays.asList: Разрешает null элементы. 🔴List.of: Враждебен к null. Если попытаетесь передать null, мгновенно получите NullPointerException. Это сделано специально, чтобы избежать ошибок в логике. 3. Связь с исходным массивом (Reference vs Copy) Если вы создаете список из существующего массива:

String[] arr = {"One", "Two"};
var list1 = Arrays.asList(arr);
var list2 = List.of(arr);

arr[0] = "Zero"; // Меняем исходный массив

System.out.println(list1); // ["Zero", "Two"] <- Изменился вслед за массивом!
System.out.println(list2); // ["One", "Two"]  <- Остался прежним

Arrays.asList работает как "окно" (view) в массив. List.of создает защитную копию данных. 🏁 Итог 🔴Используйте List.of() в 99% случаев. Это безопаснее, быстрее и потребляет меньше памяти. 🔴Используйте Arrays.asList(), только если вам нужны null-ы или вы намеренно хотите, чтобы изменения в исходном массиве отражались в списке (редкий кейс). #Java #CodeTips #InterviewQuestions #JavaCore 👉 @java_geek