Java Geek
2.54K subscribers
269 photos
1 file
23 links
Практичные советы, лайфхаки и код для Java-разработчиков. Каждый пост — реальная польза. Учим Java на примерах.

По всем вопросам @evgenycarter
Download Telegram
🔍 Завтра тестовое собеседование с Java-разработчиком

2 июля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot

Реклама. ООО "ШОРТКАТ", ИНН: 9731139396, erid: 2Vtzqwfa2Fy
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 Spring Native Image vs. Обычное Spring-приложение: В чем разница и зачем это нужно?

Разбираемся, как нативные образы меняют правила игры для Spring-приложений! 👇

Обычное Spring-приложение (JVM-based):

Когда вы запускаете классический Spring Boot на Java Virtual Machine (JVM), происходит следующее:

🔵JVM запускается первой: Прежде чем ваш код заработает, JVM должна инициализироваться и загрузить классы. Это занимает время.
🔵Динамическая природа: JVM позволяет динамически загружать классы и использовать рефлексию в рантайме. Это гибко, но ресурсозатратно.
🔵"Прогрев" (Warm-up): Для пиковой производительности JVM нужно время на JIT-компиляцию кода.
🔵Большой объем памяти: JVM сама по себе требует значительного объема RAM.
🔵Размер артефакта: JAR-файл содержит байт-код, который JVM затем обрабатывает.


Spring Native Image (с GraalVM Native Image):

Это компиляция вашего Spring-приложения в самостоятельный нативный исполняемый файл, не требующий JVM!

🔵Компиляция AOT (Ahead-of-Time): Весь код (включая Spring и JDK) компилируется в машинный код на этапе сборки. Все оптимизации происходят заранее.
🔵Мгновенный старт: Нет JVM и "прогрева"! Нативные образы стартуют мгновенно (от долей секунды). Идеально для:
🔵Серверлес-функций (Lambda, FaaS): Быстрый старт снижает задержки и стоимость.
🔵Микросервисов: Для быстрого масштабирования.
🔵Batch-задач: Быстрый запуск и завершение.
🔵Низкое потребление памяти: Отсутствие JVM значительно сокращает RAM. Экономия облачных ресурсов и возможность запускать больше приложений на сервере.
🔵Меньший размер образа: Включается только используемый код без JVM, что упрощает развертывание в контейнерах.
🔵Статический анализ: GraalVM глубоко анализирует код, исключая неиспользуемые части.


Недостатки (куда без них?):

🔵Длительное время сборки: AOT-компиляция занимает значительно больше времени.
🔵Сложности с динамикой: Рефлексия и прокси требуют дополнительных настроек (runtime hints), хотя Spring Boot 3 упрощает это.
🔵Отсутствие некоторых инструментов мониторинга: Привычные JVM-инструменты (JMX, JFR) не поддерживаются напрямую.
🔵Специфичность платформы: Нужен отдельный образ для каждой ОС и архитектуры.


Когда стоит использовать Spring Native Image?

🔵Когда время старта и потребление памяти критичны.
🔵Для микросервисов, серверлес-функций и batch-задач.
🔵Для контейнеров и облачных сред с оплатой по потреблению.

👉 @java_geek
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
🧠 Неожиданный анти-паттерн в Spring Boot: @EventListener как тихий убийца производительности

В Spring удобно использовать @EventListener для реактивных действий — казалось бы, удобно и "чисто". Но есть нюанс.

📌 По умолчанию все @EventListener вызываются синхронно и в том же потоке, где был опубликован ивент через ApplicationEventPublisher.

Это значит:


@Component
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;

public void createOrder(Order order) {
// 1. Сохраняем заказ
orderRepository.save(order);

// 2. Публикуем ивент
publisher.publishEvent(new OrderCreatedEvent(order));
// 3. До выхода из метода все @EventListener уже будут выполнены
}
}


А теперь представим, что слушатель делает что-то тяжёлое:


@EventListener
public void sendEmail(OrderCreatedEvent event) {
emailService.sendConfirmation(event.getOrder());
}


⚠️ Если sendConfirmation висит на внешнем SMTP или уходит в сеть — метод createOrder будет ждать его завершения!

💡 Решение: сделать обработку асинхронной.

Просто добавьте @Async:


@Async
@EventListener
public void sendEmail(OrderCreatedEvent event) {
emailService.sendConfirmation(event.getOrder());
}


И не забудьте включить поддержку @Async:


@EnableAsync
@Configuration
public class AsyncConfig {}


📌 Теперь @EventListener будет исполняться в отдельном потоке из TaskExecutor, и createOrder не будет блокироваться.

👉 @java_geek
👍72
🧠 Ленивая инициализация @Component в Spring Boot

По умолчанию Spring инициализирует все бины при старте приложения. Это удобно, но может замедлить запуск, особенно если есть тяжёлые компоненты — например, коннекторы к внешним сервисам, парсеры, или тяжёлые кэши.

📌 Решение — использовать ленивую инициализацию (@Lazy):


@Component
@Lazy
public class HeavyComponent {
public HeavyComponent() {
System.out.println("HeavyComponent инициализирован");
}
}


Теперь бин будет создан только при первом обращении.

Можно также внедрять лениво:


@Component
public class SomeService {
private final HeavyComponent heavy;

public SomeService(@Lazy HeavyComponent heavy) {
this.heavy = heavy;
}
}


💡 Если у вас много таких компонентов — можно включить ленивую инициализацию глобально:


spring:
main:
lazy-initialization: true


⚠️ Осторожно: это может скрыть ошибки конфигурации до первого использования бина, что усложняет отладку. Используйте осознанно.

Идеально подходит для:
— микросервисов с быстрым стартом
— CLI-инструментов на Spring Boot
— задач с heavy startup logic

👉 @java_geek
👍6
🔍Тестовое собеседование с Java-разработчиком из Oracle завтра

23 июля(уже сегодня!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Алексей Ушаровский, разработчик с опытом работы в Oracle и EPAM, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Алексей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Алексею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
Объясните концепцию наследования в Java на примерах

Наследование — один из ключевых принципов объектно-ориентированного программирования в Java. Оно позволяет одному классу наследовать свойства и методы другого класса, что способствует повторному использованию кода и устанавливает отношение «родитель–потомок» между классами.

Например, класс Car может наследоваться от общего класса Vehicle. В этом случае Car может вести себя так же, как Vehicle, с точки зрения атрибутов и методов:

- Класс Vehicle имеет такие члены, как year и make, а Car добавляет дополнительный член transmission.
- Класс Vehicle имеет метод move, а Car переопределяет move, добавляя поведение, характерное для автомобилей.

👉 @java_geek
👍7
Разница между операторами break и continue.

break и continue — это важные ключевые слова для управления потоком выполнения циклов в Java.

Оператор break используется для немедленного завершения всего цикла, игнорируя оставшийся код в нём. Он полезен, когда нужно досрочно прервать цикл при выполнении определённого условия.
Например, я использую break для отладки, чтобы выполнить только одну итерацию длинного цикла.

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

Пример кода:


for (int i = 0; i < 5; i++) {
if (i == 2) {
continue; // Skip iteration when i is 2
}
if (i == 4) {
break; // Exit loop when i is 4
}
System.out.println(i);
}


👉 @java_geek
5👍3
Что такое перегрузка методов в Java?

Перегрузка методов — это мощный приём, который позволяет классу иметь несколько методов с одинаковым именем, но разными параметрами. Это даёт объектам класса возможность обрабатывать близкие (почти одинаковые) задачи, но с разными входными данными.

Вот очень короткий пример, демонстрирующий перегрузку методов:


class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}


В этом примере класс Calculator имеет два метода add:
один принимает два целых числа (int) и возвращает сумму в виде целого числа,
а другой — два числа с плавающей запятой (double) и возвращает сумму в виде double.

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

👉 @java_geek
3👍3
Collections.nCopies()

Collections.nCopies() создаёт неизменяемый список, содержащий заданное количество копий одного и того же объекта. Это полезно для инициализации, заглушек и создания шаблонных коллекций.

👉 @java_geek
👍3
🔍 Завтра тестовое собеседование с Java-разработчиком

20 августа (уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир
→ @shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
Java Tip: Как работает var в Java

С версии Java 10 появился ключевое слово var. Оно упрощает код, автоматически выводя тип переменной:


var list = new ArrayList<String>();
list.add("Hello");
list.add("Java");

// Компилятор понимает, что list -> ArrayList<String>


Удобно: меньше шаблонного кода.

Важно: var не значит "динамическая типизация"! Тип фиксируется при компиляции.

📌 Используй var, если тип переменной очевиден - код станет чище.

👉 @java_geek
5
Какова цель ключевого слова final, когда оно используется с переменной?

Ключевое слово final в Java — это модификатор, который может применяться к переменным, методам и классам. Когда оно используется с переменной, это делает её неизменяемой, другими словами — константой. Например, переменная PI объявлена как final в классе ниже:


public class CircleCalculator {
private final double PI = 3.14159;
public double calculateArea(double radius) {
return PI * radius * radius;
}
}


👉 @java_geek
👍4
Class.getRecordComponents()

Class.getRecordComponents() возвращает информацию о компонентах записи (record). Это полезно для рефлексии, сериализации и автоматического отображения данных в Record-классах.

👉 @java_geek
👍21
⌨️ Как вырасти до Мидла или Синьора в два раза быстрее?

👨‍💻Просто хорошо работать работу не достаточно. Ты делаешь то, что нужно компании, а не то, что повысит твой грейд

Лучший способ вырасти — это персональный план развития от Senior-инженера из БигТеха.

Вот как все работает:
1️⃣ Мок-интервью 1-на-1: Час реалистичного собеса с Senior-инженером из Иннотеха, Сбера или другого бигтеха
2️⃣ Честный фидбек: созвонимся и расскажем твои точки роста, оценим грейд и потенциальный уровень зарплаты
3️⃣Персональный план развития: не просто «учите алгосы», а роадмап с конкретными темами, который приведет тебя к желаемому грейду или офферу

Мы в ШОРТКАТ провели уже почти 1000 таких мок-интервью и получили оценку 4.9/5, поэтому знаем о чем говорим.

📈 Да, и все это за 900 рублей. Почему так дешево?
Мы хотим, чтобы у каждого была возможность проверить в деле наш сервис, а потом уже доверить нам свое развитие.

Переходи в нашего бота и забирай свой мок за 900 рублей →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤡1
Как прогреть кэши в Spring Boot, чтобы убрать «холодный старт»

🧠 Идея простая: не лезьте напрямую в CacheManager. Прогревайте через те же @Cacheable методы, которые используются в рантайме - так вы не обходите unless, ключи и TTL провайдера.


📌 Минимальная реализация (Boot 3+, Java 17/21)


// Сервис с кэшем
@Service
public class CatalogService {
@Cacheable(cacheNames = "products", key = "#id", unless = "#result == null")
public Product getById(Long id) {
// дорогой вызов в БД/REST
}
}

// Прогрев после полной готовности приложения
@Component
@RequiredArgsConstructor
public class CacheWarmup {

private final CatalogService catalog;

@EventListener(org.springframework.boot.context.event.ApplicationReadyEvent.class)
public void warm() {
if (!isEnabled()) return;

var hotIds = loadHotProductIds(); // топ-ключи из БД/конфига
try (var exec = java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor()) {
// ограничьте параллелизм при необходимости (Semaphore)
hotIds.stream()
.map(id -> java.util.concurrent.CompletableFuture.supplyAsync(() -> catalog.getById(id), exec))
.forEach(java.util.concurrent.CompletableFuture::join);
}
}

private boolean isEnabled() { return true; } // читайте из настроек/профиля
private List<Long> loadHotProductIds() { return List.of(1L,2L,3L,4L,5L); }
}


application.yml:


spring:
cache:
type: caffeine
cache-names: products
caffeine:
spec: maximumSize=10000,expireAfterWrite=1h,recordStats
# Быстрый прогрев без блокировки потоков ОС (Java 21+)
spring.threads.virtual.enabled: true


💡 Где брать «горячие» ключи:

топ-N по обращениями за последние 24–72 часа (лог/метрики);
статические справочники (страны, тарифы);
ключи, которые дергают критичные эндпоинты / главная страница.


⚠️ Важные нюансы:

Не блокируйте старт сервиса надолго. Делайте прогрев асинхронно и с лимитом параллельных задач (Semaphore/FixedThreadPool).
Падения прогрева ≠ падения приложения. Оберните в try/catch, логируйте, но не валите контекст.
Health/Readiness. Если нужен «строгий» запуск, проверяйте готовность только по минимальному набору ключей, а остальное догревайте фоном.
Кластеры. В Redis/общем кеше греть можно с одного инстанса (feature-flag), чтобы не дублировать трафик. В локальных (Caffeine) - грейте на каждом.
Обновление после прогрева. Для данных, которые быстро стареют, добавьте @Scheduled обновление (@CacheEvict/@CachePut) или фоновые рефреши.

👉 @java_geek
👍3
Array vs ArrayList

Выбор между Array (стандартным Java-массивом) и ArrayList зависит от специфики задачи на Java, которую требуется решить. Помните о следующих особенностях этих типов:

☕️ Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayList может динамически изменяться;
☕️ Массивы Java работают гораздо быстрее, а в ArrayList намного проще добавлять/удалять элементы;
☕️ При работе с Array велика вероятность получить ошибку ArrayIndexOutOfBoundsException;
☕️ У ArrayList только одно измерение, а вот массивы Java могут быть многомерными.

👉 @java_geek
👍42
Можно ли перегружать метод main() в Java?

Да, метод main() может быть перегружен, но JVM или виртуальная машина Java вызывает только исходный метод main().

👉 @java_geek
👍6
Что такое стек-трейс?

Стек-трейс (stack trace) представляет собой список вызовов методов в обратном хронологическом порядке, начиная с метода, в котором произошло исключение. Стек-трейс позволяет отследить, какие методы были вызваны перед возникновением исключения, и предоставляет информацию о местоположении, где произошло исключение.

👉 @java_geek
👍3
Какая проблема возникнет с этим кодом?

Ответ: Данный код не с компилируется.

Этот вопрос на знание иерархии исключений, в данном случае FileNotFoundException унаследован от IOException, первый catch будет перехватывать все исключения и в следующий блок catch управление не будет передано.

👉 @java_geek
👍21
🔍Тестовое собеседование с Java-разработчиком из МТС уже завтра

17 сентября(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Илья Аров, старший разработчик в МТС, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Илья будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Илье

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👎1
Можно ли указывать конструктор внутри Enum?

Да, конечно. Именно через конструктор и задаются значения внутренних переменных enum.

В качестве примера добавим два поля - ageFrom и ageTo - чтобы обозначить возрастные рамки для каждой роли.

👉 @java_geek
👍1