Собеседование Java-разработчика охватывает семантику языка, коллекции, многопоточность, Spring и умение рассуждать о JVM. Ниже — вопросы, которые задают чаще всего, каждый с эталонным ответом. Middle: более глубокое понимание, оптимизацию и реальные рабочие ситуации.
1
Объясните контракт equals() и hashCode(). Что ломается при его нарушении?
Ответ
Если два объекта равны по equals(), они обязаны возвращать одинаковый hashCode; неравные объекты могут совпадать по хешу, но в идеале не совпадают. Нарушение ломает все хеш-коллекции: объект попадает в один бакет, а последующий поиск считает другой хеш, идёт в другой бакет и сообщает, что записи нет. Классический баг — изменить поле, участвующее в hashCode, уже после добавления объекта в множество.
2
Что такое garbage collection и какие сборщики есть в JVM?
Ответ
GC освобождает память кучи для объектов, до которых больше нельзя добраться от GC roots. Куча поколенческая — большинство объектов умирают молодыми, поэтому минорные сборки в young generation дёшевы. Из сборщиков есть Serial и Parallel для пропускной способности, G1 как современный дефолт с балансом паузы и throughput, а также ZGC и Shenandoah для очень больших куч с паузами меньше миллисекунды.
3
В чём разница между checked и unchecked исключениями?
Ответ
Checked наследуются от Exception и обязаны быть объявлены или обработаны — они описывают восстановимые ситуации, к которым вызывающий код должен готовиться, например IOException. Unchecked наследуются от RuntimeException и сигналят об ошибках программиста: NullPointerException, IllegalArgumentException. На практике совет такой: unchecked для багов, checked умеренно, потому что принуждение обрабатывать исключение в каждом вызове чаще всего рождает пустые catch-блоки.
4
Что такое Dependency Injection в Spring и какие есть типы инъекции?
Ответ
DI означает, что объект получает зависимости от контейнера, а не создаёт их сам, — это развязывает классы и делает их тестируемыми. Spring поддерживает инъекцию через конструктор, сеттер и поле. Предпочтительна конструкторная: зависимости становятся final, объект никогда не существует в недостроенном виде, и класс не может скрыть разрастающийся список зависимостей так, как это позволяет инъекция в поле.
5
В чём разница между @Component, @Service, @Repository и @Controller?
Ответ
Все четыре регистрируют класс как бин Spring и технически взаимозаменяемы для сканирования. Различаются намерением и тем, что Spring к ним добавляет: @Repository транслирует исключения персистентности в иерархию DataAccessException, @Controller и @RestController участвуют в маппинге запросов, а @Service — семантическая пометка бизнес-логики. Использование конкретной аннотации документирует слой и включает нужное поведение.
6
Что такое транзакция? Объясните уровни изоляции.
Ответ
Транзакция — это единица работы, удовлетворяющая ACID: она либо целиком коммитится, либо откатывается. Уровни изоляции меняют консистентность на параллелизм: Read Uncommitted допускает грязные чтения, Read Committed их исключает, Repeatable Read дополнительно исключает неповторяющиеся чтения, Serializable убирает и фантомы ценой пропускной способности. Большинство систем работают на Read Committed и закрывают остаточные аномалии оптимистичными блокировками.
7
В чём разница между ArrayList и LinkedList? Когда выбрать каждый?
Ответ
ArrayList опирается на расширяемый массив: доступ по индексу O(1), но вставка и удаление в середине сдвигают элементы. LinkedList — двусвязный список: вставка и удаление у известного узла O(1), зато доступ по индексу O(n), и каждый элемент несёт накладные расходы узла. На практике ArrayList выигрывает почти всегда, потому что локальность кеша процессора важнее теоретической сложности; LinkedList полезен в основном как Deque.
8
Что такое volatile и synchronized? Когда использовать каждый?
Ответ
volatile гарантирует видимость — запись сразу видна другим потокам, а чтения не кешируются — но не даёт атомарности, поэтому volatile-счётчик всё равно теряет инкременты. synchronized даёт и видимость, и взаимное исключение, так что блок в каждый момент выполняет только один поток. volatile берут для простого флага, который читают многие потоки; synchronized или Atomic — когда операция это чтение-изменение-запись.
9
Объясните N+1 problem в Hibernate и как её избежать.
Ответ
N+1 возникает, когда вы одним запросом достаёте N родительских сущностей, а затем лениво обращаетесь к коллекции у каждой, порождая N дополнительных запросов. Обычно это выглядит как страница, которая летает на трёх записях в тестах и намертво встаёт на трёх тысячах. Лечится JOIN FETCH в JPQL, entity graph, батчингом через @BatchSize или проекцией, которая выбирает ровно те поля, что нужны представлению.
10
В чём разница между String, StringBuilder и StringBuffer?
Ответ
String неизменяем, поэтому каждая конкатенация создаёт новый объект — сборка строки в цикле через + квадратична по времени. StringBuilder это изменяемый буфер и правильный инструмент для такого цикла. StringBuffer то же самое, но с synchronized-методами; по сути это легаси, потому что делить изменяемый буфер между потоками приходится редко, и обычно всё равно нужна более крупная блокировка.
🦎
Прочитать ответы недостаточно
На собеседовании ты говоришь вслух под давлением. Кем задаст эти же вопросы, оценит каждый ответ и покажет, что именно подтянуть.
Пройти интервью Middle Java Backend →Бесплатно · 3 интервью в месяц