Библиотека собеса по Java | вопросы с собеседований
前往频道在 Telegram
Вопросы с собеседований по Java и ответы на них. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/08c603b6 Для обратной связи: @proglibrary_feeedback_bot
显示更多6 485
订阅者
+124 小时
+87 天
+1330 天
帖子存档
❓ Что такое N+1 проблема в Hibernate и чем JOIN FETCH отличается от @EntityGraph?
Загружаешь список из N сущностей, а потом для каждой Hibernate делает отдельный SELECT за связанной коллекцией. 1 запрос на список + N запросов на детали = катастрофа на проде.
// Загружаем 100 заказов — получаем 101 запрос
List<Order> orders = orderRepo.findAll();
orders.forEach(o -> o.getItems().size()); // LAZY — N запросов здесь
JOIN FETCH vs @EntityGraph
JOIN FETCH — пишешь в JPQL явно:
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :s")
List<Order> findWithItems(@Param("s") Status s);
Гибко, но размножается: для каждой комбинации связей нужен свой метод.
@EntityGraph — декларативно, переиспользуемо:
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByStatus(Status status);
⚠️ Узкие места
JOIN FETCH с коллекциями → если fetch двух коллекций одновременно → MultipleBagFetchException. Hibernate не умеет делать два JOIN FETCH на Bag-коллекции в одном запросе.
@EntityGraph → на больших данных легко получить декартово произведение в результирующем SQL и огромный набор дублирующихся строк. Используй DISTINCT или Set вместо List.
🐸 Библиотека собеса по Java
#spring❓ Что происходит с @Async методом, если он вызывается из того же класса?
Self-invocation убивает прокси
Spring создаёт AOP-прокси вокруг бина. Когда ты вызываешь this.sendEmail(), вызов идёт напрямую к объекту, минуя прокси. @Async не срабатывает, метод выполняется синхронно в том же потоке. Никакого исключения — просто тихий фейл.
private метод — та же история.
CGLIB-прокси не может переопределить private метод. Spring либо кидает предупреждение в лог, либо молча игнорирует аннотацию.
⚠️ Ещё один подводный камень
Исключения внутри @Async метода не пробрасываются к вызывающему коду. Если метод возвращает void, исключение просто теряется (уходит в AsyncUncaughtExceptionHandler). Если Future — исключение обёрнуто и бросится только при вызове get().
📌 Как правильно
— Вынести @Async метод в отдельный бин и вызов пойдёт через прокси
— Настроить AsyncUncaughtExceptionHandler для обработки ошибок
— Возвращать CompletableFuture если нужно отслеживать результат
🐸 Библиотека собеса по Java
#spring
❓ Чем отличается @Transactional(propagation = REQUIRES_NEW) от NESTED?
И когда использование NESTED может неожиданно упасть?
REQUIRES_NEW — полностью приостанавливает текущую транзакцию и открывает новую, независимую. Свой коннект к БД, свой коммит и роллбэк. Внешняя транзакция об этом не знает.
NESTED — работает внутри той же транзакции через savepoint. Если вложенный метод откатится — откат только до savepoint'а, внешняя транзакция продолжает жить. Один коннект, один итоговый коммит.
⚠️ Когда NESTED неожиданно падает
1. JPA + Hibernate
JpaTransactionManager не поддерживает savepoints из коробки — получишь NestedTransactionNotSupportedException. Нужна явная донастройка, и то ненадёжно.
2. База не умеет savepoints
MySQL с движком MyISAM, некоторые конфигурации H2 — savepoints просто не поддерживаются на уровне драйвера.
3. Self-invocation
// proxy не задействован → NESTED полностью игнорируется
this.nestedMethod();
🐸 Библиотека собеса по Java
#spring❓ Сохраняешь Order в БД и отправляешь событие в Kafka. Как гарантировать, что событие не потеряется и не задвоится?
✅ Outbox Pattern
Сохраняй событие в таблицу outbox в той же транзакции, что и бизнес-данные. Отдельный процесс читает таблицу и публикует в Kafka.
На стороне консьюмера — идемпотентность: проверяй event_id перед обработкой.
🐸 Библиотека собеса по Java
#patterns
❓ Какие существуют проблемы в многопоточной среде?
Основные проблемы многопоточности:
1️⃣ Race Condition — когда результат работы зависит от порядка выполнения потоков. Например, два потока одновременно изменяют одну переменную без синхронизации.
2️⃣ Deadlock — взаимная блокировка, когда потоки ждут друг друга. Классика: поток А держит ресурс 1 и ждёт ресурс 2, а поток Б держит ресурс 2 и ждёт ресурс 1.
2️⃣ Livelock — потоки активны, но не могут продолжить работу, постоянно реагируя на действия друг друга. Например, как два человека в коридоре, которые одновременно пытаются уступить дорогу.
4️⃣ Starvation — поток никогда не получает доступ к ресурсу из-за того, что другие потоки постоянно его перехватывают.
5️⃣ Memory Visibility — изменения, сделанные одним потоком, могут быть не видны другим из-за кэширования в CPU.
🐸 Библиотека собеса по Java
#concurrency
✔️ Java-тест: задача выполняется, результат теряется
Никаких ошибок в логах. Никаких алертов. Данные просто не сохраняются 👇
📦 Задание — code review
Сервис нотификаций: после оплаты заказа — отправить email и записать событие в БД. Оба действия независимы, сделали асинхронно.
@Service
@RequiredArgsConstructor
public class NotificationService {
private final EmailClient emailClient;
private final EventRepository eventRepository;
private final Executor taskExecutor;
public void notifyOrderPaid(Order order) {
CompletableFuture.runAsync(
() -> emailClient.sendOrderConfirmation(order),
taskExecutor
);
CompletableFuture.runAsync(
() -> {
Event event = Event.orderPaid(order.getId());
eventRepository.save(event);
},
taskExecutor
);
}
}
▪️ Объясни
— Почему исключения из runAsync полностью проглатываются и как это работает внутри.
— Чем отличается поведение runAsync от supplyAsync в контексте обработки ошибок.
— Как переписать код так, чтобы: (1) ошибки логировались, (2) один сбой не блокировал другую задачу, (3) вызывающий код мог знать об итоге.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise❓ Что такое наследование?
Наследование — это механизм ООП, позволяющий создавать новый класс на основе уже существующего. Новый класс (подкласс) получает все свойства и методы родительского класса (суперкласса), что обеспечивает повторное использование кода и упрощает поддержку.
Наследование реализуется с помощью ключевого слова extends. Подкласс может расширять или переопределять поведение суперкласса, а также добавлять новые поля и методы. Важно помнить, что в Java класс может наследоваться только от одного суперкласса.
🐸 Библиотека собеса по Java
#core
❓ Для чего нужен @Value в Spring?
@Value внедряет значения из property файлов или environment variables в поля, параметры методов или конструкторов.
@Value("${app.name}")
private String appName;
@Value("${app.timeout:30}")
private int timeout; // 30 - default значение
@Value("#{systemProperties['user.name']}")
private String userName; // SpEL выражение
Поддерживает
— property placeholders ${...}
— SpEL выражения #{...}
— значения по умолчанию через ":"
Альтернатива для группы свойств: @ConfigurationProperties.
🐸 Библиотека собеса по Java
#spring❓ Что такое событийно-ориентированная архитектура?
Event-driven architecture — это когда компоненты общаются через события, не зная друг о друге напрямую.
// Событие:
public record UserRegistered(User user) {}
// Listener:
@Component
public class SendWelcomeEmail {
@EventListener
public void handle(UserRegistered event) {
mailer.send(event.user().email(), "Welcome!");
}
}
// Публикация события:
applicationContext.publishEvent(new UserRegistered(user));
Зачем
✔️ Слабая связанность — UserService не знает о SendWelcomeEmail
✔️ Легко добавить новый listener без изменения существующего кода (Open/Closed)
✔️ Можно делать асинхронные listeners — просто добавь @Async
В Spring
— ApplicationEventPublisher для публикации
— @EventListener для подписки
— @TransactionalEventListener — если нужно дождаться коммита транзакции
⚠️ Подводный камень: сложно трейсить цепочку — одно событие вызывает другое.❓ Для чего используется ключевое слово this?
this — это ссылка на текущий объект внутри его метода или конструктора. Оно нужно для:
— Разрешения конфликта имён (например, между полем класса и параметром метода).
— Вызова других конструкторов этого же класса.
— Передачи текущего объекта в качестве аргумента в методы или конструкторы других классов.
— Возврата ссылки на текущий объект (часто используется в паттерне Builder или для чейнинга методов).
🐸 Библиотека собеса по Java
#core
💬 Обратная связь
Текущий уровень сложности вопросов?
🔥 — Слишком просто, хочу сложнее
👍🏼 — В самый раз
❤️ — Иногда сложновато
😁 — Часто не понимаю
🐸 Библиотека собеса по Java
✔️ Java-тест: 1 запрос в коде = 10 000 запросов в БД
Код чистый, тесты быстрые, на реальных данных — pg_stat_activity в огне 👇
📦 Задание — code review
Ручка возвращает список заказов с информацией о товарах. Работает корректно, но DBA прибежал с графиком: каждый вызов делает тысячи запросов к БД.
@Entity
public class Order {
@Id
private Long id;
private Long userId;
private LocalDateTime createdAt;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private List<OrderItem> items;
}
@Entity
public class OrderItem {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
private Integer quantity;
}
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderRepository orderRepository;
@GetMapping("/orders")
public List<OrderDto> getOrders(@RequestParam Long userId) {
List<Order> orders = orderRepository.findByUserId(userId);
return orders.stream()
.map(order -> new OrderDto(
order.getId(),
order.getItems().stream()
.map(item -> new ItemDto(
item.getProduct().getName(),
item.getQuantity()
))
.toList()
))
.toList();
}
}
▪️ Объясни
— Точную механику N+1: где и сколько раз запросы уходят в БД в этом коде.
— Почему FetchType.EAGER «решит» проблему, но создаст другую.
— Как можно решить проблему.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise👩💻 Разработка на Java требует глубокого понимания не только языка, но и принципов работы JVM, многопоточности и современных фреймворков.
🎯 Курс «Java Developer. Professional» — это структурированное обучение для разработчиков, которые хотят выйти на новый уровень, освоить актуальный стек технологий и уверенно претендовать на позиции уровня Middle+.
Вы получите 96 часов практической работы, на живых вебинарах разберете ключевые аспекты работы JVM, научитесь строить эффективные многопоточные приложения, освоите Spring WebFlux, Kafka, реактивный Postgres и Kubernetes.
📚Программа OTUS постоянно обновляется, соответствуя требованиям рынка, а диплом ценится работодателями.
➡️ Пройдите вступительное тестирование и присоединяйтесь к группе: https://clc.to/sfhRPw
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
❓ Что такое идемпотентность в REST API?
Идемпотентность означает, что повторный вызов одного и того же запроса не изменяет состояние сервера более одного раза.
Способы реализации:
— Использовать контроль состояния (если запись уже существует, не создавать её второй раз).
— Использовать HTTP методы по стандарту: GET, PUT, DELETE. Уже идемпотентны по спецификации.
— Использовать уникальный ключ (идемпотентный токен) с клиентской стороны; сервер хранит, что уже получал этот токен, и при повторе не выполняет операцию заново.
— В Spring → фильтры или interceptors проверяют заголовок Idempotency-Key и используют хранилище (БД, кеш) для управления повторными запросами.
🐸 Библиотека собеса по Java
#core
❓ Что такое DDD?
DDD (Domain-Driven Design) — подход к проектированию, при котором структура кода отражает структуру бизнес-домена.
Основные строительные блоки
Entity — объект с уникальной идентичностью. Два объекта с одним ID — один и тот же объект, даже если остальные поля разные. Пример: User, Order.
Value Object — объект без идентичности, определяется своими атрибутами. Иммутабелен. Пример: Money(100, 'USD'), Email('alice@example.com'). Два Money(100, 'USD') — одно и то же значение.
Aggregate — кластер связанных сущностей с одним корнем (Aggregate Root). Все изменения внутри агрегата — только через корень. Граница транзакции = граница агрегата. Пример: Order содержит OrderItems, но только Order — корень.
Domain Service — бизнес-операция, которая не принадлежит ни одной сущности. Пример: TransferService(fromAccount, toAccount, amount).
Repository — абстракция доступа к хранилищу для агрегатов. Один репозиторий — один агрегат.
Domain Event — факт, произошедший в домене. OrderPlaced, PaymentFailed.
Bounded Context — явная граница, внутри которой модель имеет единое значение. User в контексте Billing ≠ User в контексте Shipping.
#patterns
🔴 Завтра тестовое собеседование с Java-разработчиком
1 апреля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.
Как это будет:
📂 Виктор Анохин, старший разработчик из WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Виктор будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Виктору
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
❓ В чём разница между IO и NIO?
IO (Input/Output) — это традиционная модель потоков, использующая блокирующие операции для чтения и записи данных, что может привести к задержкам, если операция занимает много времени.
NIO (New IO) был введён в JDK 1.4 для улучшения производительности, предлагая неблокирующие каналы и буферы, а также поддержку асинхронных операций, что позволяет эффективно работать с большими объёмами данных или многими соединениями одновременно.
🐸 Библиотека собеса по Java
#core
❓ Для чего нужен volatile?
volatile — это модификатор переменной, который гарантирует:
▪️ Видимость изменений между потоками
▪️ Запрет кэширования в регистре потока
Использовать стоит, когда:
▪️ Есть несколько потоков, которые читают/пишут одну переменную.
▪️ Нет сложных операций над этой переменной (например, инкрементов, которые требуют атомарности).
volatile не обеспечивает атомарности, поэтому для операций «чтение-модификация-запись» нужны синхронизация или атомарные типы (AtomicInteger, AtomicReference и т.п.).
🐸 Библиотека собеса по Java
#concurrency
💬 Обратная связь
Ежемесячный опрос по грейдам. Проверим, выросло ли число senior'ов.
Ваш грейд:
🔥 — Senior
👍🏼 — Middle
❤️ — Junior
😁 — Ещё учусь
🐸 Библиотека собеса по Java
😱 Если ваш продукт не умеет отдавать данные в формате, понятном AI-агенту, то вас просто не существует
Скрипт не будет кликать по красивым кнопкам в браузере, он уйдёт к конкуренту с нормальным API. Перестроить архитектуру под машинных клиентов — это уже не хайп, а необходимое условие сохранения конкурентоспособности.
Как адаптировать продукт и не исчезнуть из выдачи:
— интегрировать
MCP и A2A-взаимодействие, чтобы агенты могли вас читать;
— научиться контролировать стоимость (лимиты, кэш, роутинг между моделями);
— настроить AgentOps: трейсинг, логирование и отлов регрессий.
Всё это ждёт вас на обновлённом курсе «Разработка AI-агентов». Мы специально сделали фокус на утилитарном инжиниринге и production-ready решениях.
Кстати, до 29 марта можно забрать курс с большой скидкой, и стоит поторопиться — мест на потоке всё меньше.
Зафиксировать цену и начать деплоить агентов без слива бюджета 👈
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
