сеньорчикОткрыть в Telegram
← блог
7 июля 2026 г.

Вопросы на собеседовании Java: разбор от junior до middle

Собеседование на Java-разработчика редко бывает про «напиши сортировку». Куда чаще это разговор по кругу: ядро языка, коллекции, многопоточность, немного JVM, а если в вакансии есть Spring, то и он. Проверяют не зубрёжку, а понимаете ли вы, что происходит под капотом.

Ниже вопросы, которые задают чаще всего, с коротким разбором и пометкой, где это спросят у джуна, а где начинается территория миддла. Java 17/21, но большая часть верна для любой современной версии.

Ядро языка: с этого начинают всех

Джуниорская часть, на которой валятся неожиданно многие.

Контракт equals и hashCode. Самый частый вопрос ядра, и правильный ответ обязателен. Если переопределили equals, обязаны переопределить hashCode: два объекта, равные по equals, должны давать одинаковый hashCode. Нарушите, и объект «потеряется» в HashMap, потому что его будут искать не в том бакете. Обратное не требуется: одинаковый хеш у неравных объектов допустим, это просто коллизия.

== против equals для строк. == сравнивает ссылки, а equals содержимое. Отсюда классика про пул строк: строковые литералы кэшируются, поэтому "abc" == "abc" даёт true, а new String("abc") == "abc" уже false. Сравнивать строки всегда через equals.

Checked и unchecked исключения. Checked заставляют обработать себя на этапе компиляции (IOException), unchecked, наследники RuntimeException, летят молча (NullPointerException, IllegalArgumentException). Заодно спросят, чем throw отличается от throws и зачем нужен finally.

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

Коллекции: любимая тема интервьюеров

Здесь junior плавно переходит в middle.

Как устроен ArrayList внутри. Это динамический массив. Доступ по индексу O(1), а вставка в середину O(n), потому что элементы приходится сдвигать. Когда массив заполняется, он пересоздаётся с запасом, и старое содержимое копируется. Отсюда амортизированная O(1) на add в конец.

ArrayList или LinkedList. Вопрос-провокация. Правильный ответ обычно «в реальной жизни почти всегда ArrayList». LinkedList выигрывает только в узком сценарии частых вставок и удалений в начале или середине при последовательном обходе, но проигрывает по памяти и по кэш-локальности. Кандидат, который бодро отвечает «LinkedList быстрее на вставках», не подумав про доступ по индексу за O(n), выдаёт поверхностное знание.

Как работает HashMap (middle). Массив бакетов, номер бакета по hashCode ключа. Коллизии складываются в список внутри бакета, а с восьми элементов список превращается в сбалансированное дерево, чтобы худший случай не деградировал до O(n). Отсюда и требование к контракту hashCode, и вопрос про то, что будет при плохой хеш-функции.

Многопоточность: где начинается middle

Тема, которую джуну прощают, а с миддла требуют.

start() против run(). Ловушка, на которой попадаются. start() заводит новый поток и уже в нём вызывает run(). А прямой вызов run() выполнит код в текущем потоке, без всякой многопоточности. Разница между «я запустил поток» и «я просто вызвал метод».

synchronized, wait и notify. Как работает монитор объекта, почему wait() вызывают только внутри synchronized и в каком состоянии оказывается поток, который его вызвал (ждёт и отпускает монитор, в отличие от sleep). Дальше пойдут volatile, видимость изменений между потоками и happens-before.

Если тема идёт тяжело, это нормально для джуна, но к миддлу многопоточность придётся понимать не по верхам, а с примерами гонок и дедлоков.

JVM и память: проверка на глубину

Где что лежит. Объекты живут в куче (heap), а локальные примитивы и ссылки на объекты в стеке потока. Классический вопрос, за которым тянется разговор про сборку мусора: какие поколения есть в heap, почему объекты делят на молодые и старые, что такое stop-the-world.

metaspace против PermGen. С Java 8 метаданные классов переехали из PermGen в metaspace, который живёт уже не в heap, а в нативной памяти и растёт динамически. Знание таких деталей отличает того, кто читал про JVM, от того, кто только слышал.

Stream API и функциональщина

Современный код на Java без этого не пишут, поэтому спрашивают с джуна.

Функциональный интерфейс это интерфейс с одним абстрактным методом, под который подставляется лямбда (Runnable, Comparator, Function). Лямбда захватывает effectively final переменную: локальную переменную, которую после инициализации не меняют. Спросят, почему нельзя захватить изменяемую, и как это связано с потокобезопасностью.

Дальше идут цепочки map/filter/collect, ленивость стримов (ничего не считается, пока не вызван терминальный оператор), группировка через Collectors.groupingBy. И Optional как способ не ловить NullPointerException руками.

Spring: если он есть в вакансии

Когда в описании фигурирует Spring, треть собеса будет про него.

Инверсия управления и DI. Не вы создаёте зависимости через new, а контейнер Spring создаёт объекты и передаёт их вам. Это и есть IoC: управление жизненным циклом отдано фреймворку.

Внедрение через конструктор, а не в поле. Любимый вопрос про хороший тон. Конструкторное внедрение делает зависимости обязательными и видимыми, позволяет пометить поле final и легко тестировать класс без поднятия контекста. Внедрение прямо в поле через @Autowired прячет зависимости и усложняет тесты.

Как готовиться

Java-собес широкий: пробежать по верхам не выйдет, дыры вылезут на первом же уточняющем вопросе. И читать мало: формулировку «как устроен HashMap» надо не узнавать, а уметь рассказать своими словами, с ходу.

Ставится это практикой в формате вопрос-ответ-разбор. Ровно так собран трек Java в Сеньорчике: ядро языка и JVM, коллекции, многопоточность, Stream API, Spring и Hibernate идут короткими сессиями, а движок возвращает темы, где вы плывёте. Ещё живые вопросы с разбором по грейдам, в подборке для Java-разработчика, а к алгоритмической секции готовит разбор литкода. Начать можно бесплатно.