Java Core: вопросы с ответами
161 разобранных вопросов по теме «Java Core». Каждый — с правильным ответом и пояснением.
- ConcurrentModificationException: причина и решение?
Модификация коллекции во время итерации не через итератор (fail-fast, в том числе в одном потоке — например, list.remove в for-each). modCount-счётчик: при следующем next() проверяется. Решения: Iterator.remove(), CopyOnWriteArrayList, ConcurrentHashMap, removeIf() (Java 8+).
- Что такое Connection draining?
При выводе backend из ротации — дождаться завершения активных соединений, а не обрывать. Graceful shutdown.
- Eventual consistency — почему assertEquals сразу может упасть?
Kafka/RabbitMQ — асинхронные. Сообщение отправлено, но ещё не обработано consumer'ом. assertEquals сразу после send → данных в БД ещё нет. Решение: Awaitility с polling, или Thread.sleep (плохо — хрупко), или callback/listener.
- final, finally, finalize — три разных слова?
final: класс (нельзя наследовать), метод (нельзя переопределить), поле (нельзя переприсвоить). finally: блок try-catch-finally, выполняется ВСЕГДА, в том числе при return из try. finalize(): метод Object, вызывается GC недетерминированно перед удалением (deprecated).
- final — класс, метод, поле?
Класс — нельзя наследовать (String, Integer). Метод — нельзя переопределить. Поле — нельзя переприсвоить ссылку, но можно менять внутреннее состояние (final List — можно add/remove). effectively final — нужна для лямбд.
- final у класса, метода и поля?
Класс — нельзя наследовать. Метод — нельзя переопределить. Поле — нельзя переприсваивать (но можно менять внутреннее состояние, если поле — ссылка на mutable-объект).
- Generics: PECS. Что означает ? extends T и ? super T?
Producer Extends, Consumer Super. extends — можно читать, нельзя писать; super — наоборот.
- Generics: type erasure, PECS?
Type erasure: в рантайме нет типового параметра. PECS: Producer Extends (читать), Consumer Super (писать). Wildcard <?> — только чтение как Object.
- Generics: type erasure и PECS?
Дженерики стираются компилятором (type erasure) — в рантайме нет информации о типовом параметре. PECS: Producer Extends, Consumer Super. List<? extends Number> — читать, List<? super Integer> — писать.
- Generics — зачем нужны?
Типобезопасность на этапе компиляции и устранение ручных приведений типов. Без generics: List list = new ArrayList(); list.add(123); String s = (String)list.get(0) → ClassCastException в рантайме. С generics ошибка ловится компилятором.
- Что такое Health checks?
Active: балансер периодически проверяет backend (HTTP GET /health, TCP connect). Passive: считает ошибки от реальных запросов. Комбинация — best practice.
- Integer cache: почему valueOf(100)==valueOf(100) true, а valueOf(200) false?
IntegerCache кэширует значения от -128 до 127. valueOf(100) возвращает один и тот же объект из кэша → == сравнивает ссылки → true. valueOf(200) вне кэша, создаёт новый объект каждый раз → разные ссылки → false.
- Java 17/21: что нового для повседневной разработки?
Records: неизменяемый data-класс с автоматическими equals/hashCode/toString и финальными полями. Sealed Classes: ограничение иерархии явным списком permits (exhaustive switch). Pattern Matching: instanceof без ручного cast со связыванием переменной, switch по типам.
- Java — компилируемый или интерпретируемый язык?
И то, и другое. Сначала компилируется в байт-код (.class), потом JVM его интерпретирует и компилирует «горячие» места JIT-компилятором в нативный код.
- В чём разница: L3 vs L4 vs L7 балансировка?
L3 (сетевой): IP-маршрутизация, ECMP. L4 (транспортный): TCP/UDP, NAT, по IP:port без разбора содержимого. L7 (прикладной): HTTP-заголовки, URL, cookies. L4 быстрее (меньше overhead), L7 гибче.
- Что такое LazyInitializationException?
Обращение к lazy-полю после закрытия сессии (вне транзакции), когда прокси не может догрузить данные. Решения (от лучшего к худшему): DTO-проекция (SELECT NEW), JOIN FETCH (@Query), @EntityGraph(attributePaths), @BatchSize(size=100).
- Liquibase vs Flyway?
Liquibase — описание изменений декларативно в XML/YAML/JSON (а также SQL), поддерживает rollback и кросс-БД абстракцию (одно описание для разных БД). Flyway — версионированные SQL-скрипты (V1__init.sql, V2__add_table.sql), проще, но без кросс-БД абстракции, а классический rollback (undo) доступен только в платной версии.
- В чём разница: map vs flatMap?
map: T → R, один элемент → один. flatMap: T → Stream<R> с уплощением. Пример: List<List<String>> → flatMap(Collection::stream) → плоский Stream<String>. Для Optional: flatMap избегает Optional<Optional<T>>.
- Maven vs Gradle?
Maven — XML-декларатив (pom.xml), строгая структура, проще. Gradle — Groovy / Kotlin DSL, гибче, быстрее (incremental builds). В Сбере и большинстве банков — Maven, в Android и многих стартапах — Gradle.
- Optional — когда?
Для возвращаемых значений методов. НЕ для полей (не Serializable), НЕ для параметров, НЕ для коллекций (пустая лучше). Антипаттерны: Optional.get() без isPresent(), Optional в полях, Optional<List>.
- Optional — основные методы и антипаттерны?
isPresent/isEmpty — проверка. ifPresent(consumer) — действие если есть (возвращает void). orElse(default) — готовое значение по умолчанию, вычисляется всегда (eager). orElseGet(supplier) — ленивый, вызывается только при отсутствии значения. map/flatMap — трансформация (flatMap когда функция сама возвращает Optional). Антипаттерн: Optional.get() без проверки и использование Optional как поля класса или параметра метода.
- Overload vs Override — в чём разница?
Overload (перегрузка): несколько методов с одним именем, но разными параметрами в одном классе. Компилятор выбирает статически (статическое связывание). Override (переопределение): подкласс меняет реализацию метода родителя с той же сигнатурой, а вызов разрешается динамически по фактическому типу объекта (динамическое связывание).
- Parallel streams: в чём опасность?
Все parallel streams делят общий ForkJoinPool.commonPool() с числом потоков по числу ядер. Если один параллельный стрим заблокировался на I/O — пул исчерпывается и остальные ждут. Решение: кастомный ForkJoinPool, например new ForkJoinPool(4).submit(() -> stream.parallel()...).get().
- Records (Java 16+)?
Иммутабельные data-классы: record Point(int x, int y) {}. Автоматически: конструктор, getters, equals, hashCode, toString. Нельзя наследовать (implicitly final). Можно реализовывать интерфейсы.
- Records (Java 16+): можно ли от них наследоваться?
Нет. Record неявно наследуется от java.lang.Record, а Java не поддерживает множественное наследование классов.
- Records в Java — что это?
Java 14+: компактный immutable data-класс. record User(String name, int age) {} — автоматически: канонический конструктор, аксессоры по именам компонентов (name(), age()), equals, hashCode, toString по значению. Поля private final, запись неявно final — от неё нельзя наследоваться.
- Sealed Classes (Java 17+)?
Ограничение иерархии наследования: sealed class Shape permits Circle, Square. Компилятор гарантирует exhaustive switch. Связка с Pattern Matching for switch (Java 21).
- Sealed classes — зачем?
Ограничение иерархии наследования. Хорошо работает с pattern matching.
- Что такое Sticky sessions?
Привязка клиента к конкретному backend. По cookie, IP, header. Проблема: неравномерная нагрузка при скейлинге. Решение: shared session storage (Redis) или stateless (JWT).
- Что такое Stream API?
Промежуточные (ленивые): filter, map, flatMap, sorted, distinct. Терминальные: collect, forEach, count, reduce, toList. Обработка вертикальная. Стрим одноразовый.
- В чём разница: Stream API: промежуточные vs терминальные?
Промежуточные (filter, map, flatMap, sorted, distinct, peek, limit, skip) — ленивые, возвращают Stream, не выполняются пока нет терминальной. Терминальные (collect, forEach, count, reduce, findFirst) запускают конвейер и возвращают результат или void, завершая стрим.
- В чём разница: Stream API: промежуточные vs терминальные операции?
Промежуточные (filter, map, sorted) возвращают Stream и ленивые. Терминальные (collect, forEach, count, reduce) запускают pipeline.
- Stream API: промежуточные vs терминальные операции. Что такое ленивость?
Промежуточные (filter, map) не выполняются до вызова терминальной (collect, forEach).
- Stream API: что произойдёт при двух терминальных операциях?
IllegalStateException: stream has already been operated upon or closed. Стрим одноразовый — после терминальной операции закрывается. Если нужно два результата — пересоздайте стрим или используйте Collectors (например, teeing/groupingBy).
- В чём разница: Stream API: что такое, промежуточные vs терминальные?
Stream — конвейер обработки данных. Промежуточные (ленивые): filter, map, flatMap, sorted. Терминальные (запускают): collect, forEach, count, reduce. Стрим одноразовый.
- Что такое String Pool и immutability?
Pool в heap хранит уникальные литералы. new String("hi") — новый объект вне пула. intern() добавляет в пул. Immutability: безопасность, потокобезопасность, кэширование hashCode, переиспользование в пуле.
- В чём разница: String vs StringBuilder vs StringBuffer?
String: immutable, при конкатенации создаёт новый объект. StringBuilder: mutable, НЕ потокобезопасный (быстрый). StringBuffer: mutable, потокобезопасный (synchronized — медленнее). В цикле конкатенация String → O(n²) из-за создания нового объекта на каждой итерации.
- Что такое try-with-resources?
Автоматически вызывает close() в finally. Ресурс реализует AutoCloseable. Закрытие в обратном порядке объявления. Если и try, и close бросают исключение — наружу идёт исключение тела, а close-исключение подавляется (getSuppressed()).
- try-with-resources — какой интерфейс нужен?
AutoCloseable (или Closeable). Ресурсы закрываются автоматически в обратном порядке объявления.
- Что такое VRRP?
Virtual Router Redundancy Protocol. active-standby пара балансеров. При падении primary — мгновенное переключение, IP не меняется. Используется в VK Cloud.
- == vs equals для строк?
== сравнивает ссылки. equals — содержимое. Из-за String Pool два литерала "abc" == "abc" дадут true, но new String("abc") == "abc" — false (new создаёт новый объект вне пула). На собесе всегда отвечать «equals для содержимого».
- == vs equals для строк. Почему String immutable?
== сравнивает ссылки (один объект в памяти?). equals — содержимое. String immutable: безопасность (ключи HashMap, передача в файлы/БД), потокобезопасность, кэширование hashCode, String Pool.
- В чём разница: Абстрактный класс vs интерфейс?
Абстрактный класс: состояние (поля), конструкторы, один наследник. Интерфейс: контракт, множественная реализация, Java 8 — default/static методы. Когда что: общее поведение + состояние → абстрактный класс; только контракт с множественной реализацией → интерфейс.
- В чём отличие String, StringBuilder, StringBuffer?
String — immutable. Каждая конкатенация создаёт новый объект, в цикле это O(n²) по памяти. StringBuilder — mutable, не потокобезопасный, быстрый. StringBuffer — mutable + synchronized методы, потокобезопасный, но медленнее.
- В чём проблема — несколько связанных пунктов?
Несколько связанных проблем. (1) Поле не final — может быть случайно перезаписано, в том числе через reflection. Для thread-safe сервиса (а Spring singleton — он всегда thread-safe должен быть) — это потенциальная проблема: singleton один на контейнер и разделяется между потоками, поэтому изменяемое состояние даёт гонки. Решение — делать поля final/неизменяемыми и инжектить через конструктор.
- В чём разница Comparable и Comparator?
Comparable<T> — внутри класса (метод compareTo, java.lang). Описывает «естественный» порядок. Один на класс. Comparator<T> — отдельно (метод compare, java.util). Можно сделать несколько разных Comparator'ов для одного класса под разные порядки сортировки.
- В чём разница Factory Method и Abstract Factory?
Factory Method — один метод, создающий объект. Часто используется для создания разных подклассов одного типа. Например, createConnection() возвращает PostgresConnection или OracleConnection. Abstract Factory — объект-фабрика с несколькими методами, порождающий целое семейство связанных продуктов.
- В чём разница final, finally, finalize?
final — модификатор: final class/method/field (запрет наследования, переопределения, переприсваивания). finally — блок в try-catch, выполняется ВСЕГДА (даже при исключении или return), используется для освобождения ресурсов. finalize() — устаревший метод Object, вызываемый GC недетерминированно, полагаться на него для очистки ресурсов нельзя.
- В чём разница merge и rebase?
merge сохраняет историю как есть — создаёт merge-коммит, объединяющий ветки, и виден факт слияния. rebase «перематывает» твои коммиты поверх свежей основной ветки — история линейная, без merge-коммитов, но хеши коммитов переписываются, поэтому rebase нельзя применять к уже опубликованным коммитам.
- В чём разница orElse и orElseGet?
orElse(value) — eager: значение-аргумент вычисляется ВСЕГДА, даже если Optional не пустой. orElseGet(Supplier) — lazy: лямбда вызывается только если Optional пустой, поэтому для дорогих вычислений предпочтительнее orElseGet.
- В чём разница абстрактного класса и интерфейса?
Абстрактный класс может иметь состояние (поля), конструктор и реализованные методы, но унаследоваться можно ТОЛЬКО от одного. Интерфейс — контракт, описывающий «что умеет», поддерживает множественную реализацию, а с Java 8 — default-методы; его методы по умолчанию public abstract.
- В чём разница между композицией и агрегацией?
Композиция = владение (жизненные циклы связаны), агрегация = ссылка.
- В чём суть проблемы?
Это «магическое» условие — фильтр, смысл которого непонятен из кода. «Берём только нечётные цены» — это бизнес-правило или баг? Если правило — оно должно быть задокументировано или вынесено в именованный предикат с говорящим названием, чтобы намерение стало явным.
- Где видишь себя через год?
Развиваться как Java-разработчик: вырасти от стажёра до junior'а / middle, освоить более глубокие темы (микросервисы, многопоточность, БД). Если есть конкретное направление — упомяни. Главное — показать желание расти именно как специалист, а не привязываться к должности, зарплате или смене компании.
- Где хранятся примитивы и где объекты?
Примитивы как локальные переменные — на стеке. Если примитив — поле объекта, он лежит внутри объекта в куче. Все объекты — в куче (heap). Статические поля и метаданные классов — в Metaspace (с Java 8 заменил PermGen).
- Готов ли работать с legacy-кодом?
Да, и стоит это раскрыть. В банке много legacy — никуда не деться. Хорошие фразы: «Понимаю, что legacy — это часть работы», «Это полезный опыт — понять, как развивается большая система». Спокойное согласие с раскрытием показывает зрелое отношение, в отличие от отказа или требования всё переписать.
- Готов ли совмещать стажировку с учёбой?
Честно. Если готов — скажи, что обычно посвящаешь учёбе X часов в неделю, стажировке готов отдать Y. Если есть гибкость в расписании — упомяни. Сбер понимает, что стажёр — студент.
- Для чего нужен Optional?
Контейнер, который может содержать или не содержать значение. Появился в Java 8 как альтернатива null. Помогает явно показывать в сигнатуре методов, что результат может отсутствовать.
- Если поле не final при constructor injection — будет ошибка?
Нет. Spring отлично инжектит и в не-final поля. Но смысл конструктора частично теряется — поле потом можно перезаписать. Конвенция: всегда final.
- Зачем RandomGenerator вместо Random?
RandomGenerator — общий интерфейс для всех ГСЧ в Java 17+. Это часть JEP 356: Enhanced Pseudo-Random Number Generators. Реализации: ThreadLocalRandom, SplittableRandom, различные алгоритмы (Xoshiro, L128X, L64X). Позволяет выбирать генератор по свойствам (период, splittable, jumpable) через RandomGeneratorFactory; для криптостойкости по-прежнему нужен SecureRandom.
- Зачем нужны Liquibase или Flyway?
Управление версиями схемы БД. Без них: разработчики применяют SQL руками, кто-то применил, кто-то нет, на проде катастрофа. С Liquibase: changeset-ы лежат в репозитории и применяются последовательно, а факт применения фиксируется в служебной таблице. Так схема во всех окружениях приводится к одному состоянию воспроизводимо.
- К каким конструкциям применим модификатор final?
К классу (нельзя наследовать), методу (нельзя переопределить), переменной (нельзя переприсваивать).
- Как из List сделать неизменяемый?
List.copyOf(list) (Java 10+) или Collections.unmodifiableList(list) — обёртка, которая бросит UnsupportedOperationException на add/remove.
- Как написать иммутабельный класс?
final class (нельзя наследовать). private final поля. Нет сеттеров. Конструктор с defensive copy для mutable-аргументов. Геттеры возвращают defensive copy для mutable-полей (List → Collections.unmodifiableList()), иначе наружу утекает изменяемая ссылка, ведь final защищает только ссылку, а не сам объект.
- Как правильно?
Два варианта в зависимости от ответа на «зачем»: (1) Если фильтр случайный или для отладки — удалить полностью. Если ничего не сломается — фильтр был лишним. (2) Если он реально нужен и работает — оставить и задокументировать, зачем он тут. Главное — честно решить судьбу кода, а не прятать его за флагами.
- Как работает Liquibase?
(1) Описываешь changeset'ы — изменения схемы. Формат: XML, YAML, JSON или SQL. (2) При старте Liquibase подключается к БД, смотрит в таблицу DATABASECHANGELOG — какие уже применены. (3) Применяет новые changeset'ы последовательно и фиксирует их в этой служебной таблице. История хранится в БД, а не в файле changelog.
- Как работает protected через пакеты?
protected-метод виден всему своему пакету (как package-private) И всем наследникам, даже из других пакетов. То есть из другого пакета можно получить доступ только через наследование, не напрямую через ссылку. Тонкость на собесе.
- Как работает чтение файлов в Java?
Через потоки I/O: InputStream / OutputStream — для байтов. Reader / Writer — для символов (с учётом кодировки). Buffered-обёртки (BufferedReader, BufferedInputStream) — для эффективности, читают сразу пачку и снижают число системных вызовов. Потоки нужно обязательно закрывать (try-with-resources).
- Как разрешать конфликты при merge?
Git помечает конфликтные участки в файле символами <<<<<<<, =======, >>>>>>>. Вручную решаешь, что оставить, удаляешь маркеры, делаешь git add и git commit. Современные IDE (IntelliJ) дают удобный визуальный merge-tool.
- Как реализовать Singleton?
Самый простой и правильный способ — через enum (с Java 5): public enum Database { INSTANCE; public void connect() { /* ... */ } }
- Как сделать List неизменяемым?
List.of(1,2,3) — Java 9+, immutable, null запрещён. List.copyOf(list) — Java 10+, глубокая неизменяемая копия. Collections.unmodifiableList(list) — обёртка (изменения в оригинале видны!). Предпочитай List.of или List.copyOf.
- Как сортировать List по убыванию по полю?
list.sort(Comparator.comparing(User::getAge).reversed()) или с лямбдой list.sort((a, b) -> b.getAge() - a.getAge()) — но безопаснее через Comparator.comparing.
- Какие есть примитивные типы и их размеры?
byte (1), short (2), int (4), long (8), float (4), double (8), char (2), boolean (~1, JVM-зависимо).
- Какие команды Git знаешь?
git clone — скачать репозиторий. git pull — забрать и слить изменения. git push — отправить на сервер. git add — добавить файлы в staging. git commit — зафиксировать в историю. git branch — создать / посмотреть ветки.
- Какие могут быть «легитимные» причины?
Их немного, и все маловероятны. (1) Тесты на edge cases — фильтр оставили из отладочного кода. (2) Какой-то странный бизнес-юзкейс типа «скидку даём только на товары с нечётной ценой» — но это нонсенс с точки зрения маркетинга. По сути, такое поведение почти всегда подозрительно и требует разбора, а не оправдания.
- Какие модификаторы доступа есть в Java?
private — только внутри своего класса. package-private (без модификатора, default) — внутри своего пакета. protected — пакет + наследники из других пакетов. public — отовсюду.
- Какие операции бывают в Stream?
Промежуточные (intermediate) — возвращают Stream, lazy: filter, map, flatMap, distinct, sorted, peek. Терминальные (terminal) — запускают конвейер: collect, reduce, forEach, count, findFirst.
- Какие основные методы у Object?
equals(Object) — проверка равенства. hashCode() — целочисленный хэш. toString() — строковое представление. getClass() — Class-объект. clone() — копия объекта (нужно implements Cloneable). wait(), notify(), notifyAll() — межпоточное взаимодействие. finalize().
- Какие примитивные типы есть в Java?
Их 8: byte (1 байт), short (2 байта), int (4 байта), long (8 байт), float (4 байта), double (8 байт), char (2 байта, UTF-16), boolean (1 бит). У каждого примитива есть класс-обёртка: Integer, Long, Boolean и так далее.
- Какие реализации List ты знаешь?
ArrayList — на основе массива. Доступ по индексу O(1), вставка в конец амортизированно O(1), в середину O(n). Самая популярная. LinkedList — двусвязный список. Доступ по индексу O(n), но вставка/удаление по найденному узлу O(1).
- Какие функциональные интерфейсы из java.util.function ты знаешь?
Consumer<T> — принимает T, ничего не возвращает (T → void). Supplier<T> — без параметров, возвращает T (() → T). Predicate<T> — принимает T, возвращает boolean. Function<T,R> — T → R. BiFunction<T,U,R> — два параметра T и U, возвращает R.
- Какой пет-проект делал? Что было самым интересным?
Ответ через STAR. Главное — не просто описать, что сделал, а показать рефлексию: что было сложно, чему научился, что бы переделал.
- Когда использовать Optional?
Для возвращаемых значений методов, где результат может отсутствовать. НЕ для полей, параметров методов, коллекций — это антипаттерн.
- Когда использовать Optional, а когда — нет?
Для возвращаемых значений. НЕ для полей, параметров методов, коллекций.
- Когда нужен Builder?
Когда у объекта много полей (особенно опциональных), и не хочется делать конструктор с 10 параметрами. Builder делает создание объекта пошаговым и читаемым, заменяя громоздкий телескопический конструктор, и удобен для иммутабельных объектов.
- Что такое Контракт equals/hashCode?
Если a.equals(b) == true, то a.hashCode() == b.hashCode(). Обратное необязательно (коллизии допустимы). equals(null) → false. Переопределяешь один — переопределяй оба. Практический пример: положить объект в HashSet — иначе он не найдётся.
- Контракт equals/hashCode. Что сломается при нарушении?
Если a.equals(b), то hashCode совпадают. Иначе HashMap/HashSet теряют объекты.
- Лямбда-выражения: почему переменные должны быть effectively final?
Лямбда захватывает копию значения переменной, не ссылку на неё. Если переменная изменится после захвата — копия устареет → неочевидное поведение. Компилятор требует final или effectively final для защиты от ошибок.
- Модификаторы доступа — порядок?
private → (default/package-private) → protected → public. private: только в классе. default: в пакете. protected: в пакете + наследники (даже в другом пакете). public: везде.
- Можно ли в catch ловить Throwable? Стоит ли?
Можно, но не стоит — поймаешь и Error (OutOfMemoryError, StackOverflowError), которые обычно нельзя восстанавливать. Лови конкретные исключения.
- Можно ли переопределить static-метод?
Нет. Static-методы не участвуют в полиморфизме. Если в подклассе будет метод с такой же сигнатурой — это сокрытие (hiding), а не переопределение.
- Можно ли создать свой exception от RuntimeException?
Да, и это типовая практика. Большинство современных проектов делают свои исключения от RuntimeException — не загромождают сигнатуру методов throws. От Error не рекомендуется наследоваться — это «системные» ошибки JVM (например, OutOfMemoryError), которые приложению не положено генерировать или ловить.
- На какие три группы делятся паттерны?
Порождающие (Creational) — про создание объектов: Singleton, Builder, Factory Method, Abstract Factory, Prototype. Структурные (Structural) — про композицию объектов: Adapter, Decorator, Proxy, Facade, Composite, Bridge. Поведенческие (Behavioral) — про взаимодействие и распределение обязанностей: Strategy, Observer, Command, Iterator, Template Method.
- Что такое Назови 4 принципа ООП?
Инкапсуляция — скрытие внутреннего состояния объекта, доступ только через методы. Наследование — один класс получает поля и методы другого через extends. Полиморфизм — один интерфейс, разные реализации. Абстракция — выделение существенных характеристик и сокрытие деталей реализации за общим интерфейсом.
- Опыт в Scrum / Agile?
Если в учебном проекте делали daily-стендапы и спринты — это уже опыт. Можно сказать: «В курсовой работали по двухнедельным спринтам, делали ретро в конце». Если не делали — честно: «Знаю теорию (роли Scrum Master, Product Owner, команда; спринты, стендапы, ретро), на практике пока не применял».
- От какого класса наследуются все классы в Java?
От java.lang.Object. Это корень иерархии — даже массивы и enum'ы наследуются от него косвенно.
- Почему final-поля и constructor injection — стандарт Senior?
Иммутабельность зависимостей — основа thread-safe сервиса. После создания объекта его зависимости НЕ должны меняться. final гарантирует это на уровне компилятора. Конструктор делает зависимости явными — открыл класс и сразу видишь, от чего он зависит, и это легко проверить.
- Почему shared Random может быть медленнее sequential?
В sequential один поток последовательно дёргает nextDouble — CAS всегда успешен, никаких retry. В parallel 7 потоков одновременно пытаются обновить seed через CAS: только один успевает, остальные 6 крутят retry в цикле, и это contention сводит на нет весь выигрыш от параллелизма.
- Почему String immutable?
Четыре причины: 1) безопасность — строки передаются в конструкторы файлов, БД, сетевых соединений, изменение привело бы к уязвимостям; 2) потокобезопасность — можно шарить между потоками без синхронизации; 3) кэширование hashCode; 4) корректная работа String Pool.
- Почему String в Java immutable?
Безопасность (передача в файлы, БД, сеть), потокобезопасность, кэширование hashCode, работа String Pool.
- Почему именно Сбер?
Готовы 2–3 факта: масштаб (один из крупнейших IT-работодателей России), Platform V (своя облачная платформа), SberAI (GigaChat, Kandinsky), 78% оффер после стажировки. Можешь упомянуть конкретный продукт Сбера, который тебе близок, и связать его со своим опытом.
- Разница между == и equals() для строк?
== сравнивает ссылки. equals() сравнивает содержимое. Для строк, созданных литералом, == может вернуть true из-за String Pool, но полагаться на это нельзя.
- Разница между == и equals(). Что вернёт someObj.equals(null)?
== сравнивает ссылки; equals(null) по контракту должен возвращать false.
- Что такое Расскажи жизненный цикл Maven?
validate → compile → test → package → verify → install → deploy. validate — проверка проекта. compile — компиляция. test — юнит-тесты. package — упаковка в jar/war. verify — интеграционные тесты. install — установка артефакта в локальный ~/.m2. deploy — публикация в удалённый репозиторий.
- Что такое Расскажи иерархию коллекций?
Iterable → Collection. От Collection наследуются List, Set, Queue. Map стоит ОТДЕЛЬНО — она НЕ Collection. Это пары ключ-значение, не отдельные элементы. Поэтому у Map свои методы (put, get), и она не реализует Collection.
- Что такое Расскажи контракт equals и hashCode?
Контракт equals: рефлексивность (a.equals(a) = true), симметричность (a.equals(b) ⇔ b.equals(a)), транзитивность (a=b и b=c → a=c), консистентность (повторный вызов даёт тот же результат), equals(null) = false. Согласованность с hashCode: равные по equals объекты обязаны иметь равный hashCode (обратное необязательно).
- Расскажи о себе — что говорить?
Готовь рассказ на 2–3 минуты. Структура: (1) Кто ты — имя, курс, вуз, специальность. (2) Как пришёл в Java — через что попал в программирование, какие курсы/книги читал. (3) Чем занимался — проекты и стек. (4) Сильные стороны. (5) Зачем тебе эта позиция.
- Что такое Расскажи про модификаторы доступа?
private — только в классе. (default/package-private) — в пакете. protected — в пакете + наследникам. public — везде.
- Расскажите про ООП: инкапсуляция, наследование, полиморфизм?
Будьте готовы объяснить полиморфизм пятикласснику.
- Что такое Расскажите про принципы ООП?
Инкапсуляция, наследование, полиморфизм, абстракция. На Junior достаточно объяснить «своими словами» с примерами из жизни.
- Что такое Расшифруй SOLID?
S — Single Responsibility (один класс — одна ответственность). O — Open/Closed (открыт для расширения, закрыт для модификации). L — Liskov Substitution (наследник заменяет базовый тип без поломки поведения). I — Interface Segregation (много узких интерфейсов лучше одного большого). D — Dependency Inversion (зависеть от абстракций, а не от конкретных классов).
- Сколько аппрувов нужно на merge в Сбере?
Обычно 2 аппрува от backend-разработчиков + финальный просмотр начальника отдела перед влитием. Это compliance-требование (важная сумма банковских регуляторов).
- Что такое Функциональные интерфейсы?
Один абстрактный метод. Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, UnaryOperator<T>, BiFunction, Comparator, Runnable, Callable.
- Что такое Функциональные интерфейсы и Stream API?
Один абстрактный метод (SAM). Function, Predicate, Consumer, Supplier, Comparator. Стрим: промежуточные (ленивые) → терминальные (запускают конвейер). Обработка вертикальная, не горизонтальная. Стрим одноразовый.
- Что такое Функциональный интерфейс?
Ровно один абстрактный метод (SAM). @FunctionalInterface — опциональная аннотация, но защищает от случайного добавления второго метода; лямбда компилируется и без неё. Default- и static-методы разрешены. Основные: Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, UnaryOperator<T>.
- Чем опасны типы-обёртки (Integer, Boolean)?
NPE при unboxing null: Integer i = null; int x = i; → NPE. Лишняя память: Integer = 16 байт vs int = 4 байта. Медленнее: автобоксинг создаёт объекты. IntegerCache: valueOf(127) == valueOf(127) → true, но valueOf(128) == valueOf(128) → false, поэтому сравнивать нужно через equals.
- Что такое Четыре принципа ООП своими словами?
Инкапсуляция: банкомат — не знаешь как хранит деньги, есть методы «снять», «положить». Наследование: электросамокат наследует «транспорт». Полиморфизм: кнопка «play» — разное поведение для видео, музыки, подкаста. Абстракция: выделяешь существенное и прячешь детали за общим интерфейсом.
- Что будет, если hashCode возвращает константу (например, return 1)?
Все объекты попадут в один бакет HashMap. Поиск превратится в перебор связного списка (или дерева с Java 8) — деградация с O(1) до O(n) или O(log n). Карта работает, но медленно. Это любимый подвох на собесе.
- Что будет, если hashCode() всегда возвращает одну константу?
В Java 7 — деградация HashMap до связного списка O(n). В Java 8+ при 8 коллизиях в бакете он превращается в red-black tree, O(log n). В обоих случаях производительность резко падает.
- Что будет, если переопределить только equals, но не hashCode?
Сломаются hash-структуры: HashMap, HashSet, Hashtable. Положили объект — посчитался один hashCode, переопределённый equals говорит «они равны», но другой hashCode значит другой бакет. Не найдёшь, что положил: get/contains ищет в одном бакете, а объект лежит в другом. Код компилируется, исключений нет — просто нарушен контракт equals/hashCode.
- Что вернёт someObj.equals(null)?
false по контракту. equals не должен бросать NPE на null-аргумент.
- Что выполнится в finally, если в try return?
Finally выполнится перед фактическим возвратом. Если в finally тоже return — он перебьёт значение из try (анти-паттерн).
- Что делает final для класса, метода, переменной?
Для класса — нельзя унаследовать (final class String). Для метода — нельзя переопределить в наследнике. Для переменной — можно присвоить один раз. Для поля — становится константой (часто вместе со static).
- Что делает i++?
Read-modify-write: прочитать значение, добавить 1, записать обратно. Это ТРИ операции, и они НЕ атомарны. Поэтому в многопоточной среде два потока могут одновременно прочитать одно значение и оба прибавить — итог будет +1 вместо +2, часть инкрементов теряется. Для атомарности нужны AtomicInteger или synchronized.
- Что делает mvn clean install?
clean — удаляет директорию target/. install — проходит весь жизненный цикл (compile → test → package) и кладёт артефакт в локальный репозиторий ~/.m2. Самая часто используемая команда.
- Что делает System.exit()?
Принудительно завершает JVM с кодом возврата. Не выполняются finally-блоки, не закрываются ресурсы. Использовать ТОЛЬКО в экстренных случаях. В обычной разработке — не нужен.
- Что если конфликт с коллегой?
Подход: (1) Сначала пробую решить 1-на-1, спокойно. (2) Стараюсь понять его сторону — может, я не так понял. (3) Если не получается — иду к тимлиду, не за «он плохой», а за «помогите разрулить процесс». Конструктивный путь без обвинений и публичного давления.
- Что знаешь о Сбере как о работодателе?
Экосистема: СберМаркет, Самокат, Okko, СберЗдоровье, СберДевайсы, Сбер Бизнес. Platform V — собственная low-code платформа. SberAI с GigaChat и Kandinsky. Программа стажировок Sberseasons. Большая собственная IT-экосистема со своими продуктами, платформой и AI-направлением.
- Что значит lazy в Stream?
Промежуточные операции не выполняются сразу. Они «копят» план. Терминальная операция запускает выполнение. Поэтому stream.filter(...).map(...) без terminal — не сделает ничего. Это позволяет оптимизировать выполнение.
- Что сломается, если hashCode() вернёт константу?
Все объекты попадут в один bucket HashMap. Java 7: связный список O(n). Java 8+: при 8 коллизиях И capacity ≥ 64 — перестройка в red-black tree O(log n). Это лучше O(n), но деградация по сравнению с O(1).
- Что сломается, если hashCode() вернуть константу?
Все объекты попадут в один bucket HashMap. В Java 8+ при 8 коллизиях список превращается в red-black tree, но O(log n) вместо O(1) — всё равно деградация производительности.
- Что сломается, если hashCode() — константа?
Все объекты в одном bucket. Java 7: список O(n). Java 8+: при 8 коллизиях И capacity ≥ 64 — red-black tree O(log n). Деградация по сравнению с O(1).
- Что такое ConcurrentModificationException?
Бросается итератором в fail-fast режиме, если коллекция изменилась во время итерации не через сам итератор. Реализуется через счётчик modCount коллекции и expectedModCount итератора: если при next() они разъезжаются — исключение. Срабатывает и в одном потоке, это рантайм-проверка, а не про многопоточность.
- Что такое default-методы в интерфейсах?
С Java 8 в интерфейсе можно объявить метод с готовой реализацией через ключевое слово default. Это решает проблему расширения интерфейсов без поломки совместимости: добавляешь новый метод с default-реализацией, и старые классы-наследники его наследуют, не переопределяя.
- Что такое DRY, KISS, YAGNI?
DRY — Don't Repeat Yourself. Не дублируй код, выноси в отдельный метод/класс. KISS — Keep It Simple, Stupid. Простое решение лучше навороченного. YAGNI — You Aren't Gonna Need It. Не пиши код «на будущее», который не нужен сейчас.
- Что такое functional interface? Приведите 3 примера из java.util.function?
Интерфейс с одним абстрактным методом. Примеры: Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>.
- Что такое Git Flow и Trunk-based?
Git Flow — модель с долгоживущими ветками master, develop, feature/*, release/*, hotfix/*; подходит для релизного цикла с явными версиями. Trunk-based — все работают близко к master через короткие feature-ветки с частой интеграцией.
- Что такое .gitignore?
Файл со списком путей и шаблонов, которые Git должен игнорировать. Туда обычно кладут: target/ (Maven), node_modules/, .idea/ (IntelliJ), *.log, файлы с секретами (хотя секреты лучше вообще не класть в репозиторий).
- Что такое LazyInitializationException?
Runtime-исключение, возникающее при обращении к LAZY-полю или коллекции после закрытия сессии Hibernate. Сценарий: достал Entity в @Transactional-методе, вернул наружу; транзакция и сессия закрылись — при попытке дочитать ленивое поле летит исключение.
- Что такое LazyInitializationException и как с ним жить?
Попытка обратиться к lazy-полю после закрытия сессии. Решения: JOIN FETCH в запросе, @EntityGraph, DTO-проекции, OpenSessionInView (анти-паттерн в проде).
- Что такое Observer?
Поведенческий паттерн: Subject хранит список подписчиков (Observer) и при изменении состояния сам уведомляет всех (push-модель). В Java встречается: PropertyChangeListener, java.util.Observer (устарел), Spring Events.
- Что такое pom.xml?
Project Object Model — файл конфигурации Maven-проекта. Содержит: координаты артефакта (groupId, artifactId, version), зависимости, плагины, свойства, профили. Это сердце Maven-проекта.
- Что такое Pull Request / Merge Request?
Предложение влить твою ветку в основную (master / main / develop). Коллеги делают код-ревью, оставляют комментарии, требуют изменений. После одобрения — merge. В банках обычно нужно 2 аппрува.
- Что такое record (Java 14+)?
Короткий способ описать иммутабельный DTO. Автоматически генерируются: конструктор, геттеры (имя поля без get-), equals, hashCode, toString. record User(String name, int age) {} — всё. Меньше boilerplate, чем Lombok.
- Что такое Singleton?
Гарантирует, что в системе будет один экземпляр класса, и даёт глобальную точку доступа. Используется для конфигов, логгеров, пулов соединений. В Spring каждый @Component по умолчанию — Singleton.
- Что такое STAR?
Методика структурированного рассказа о ситуации. S — Situation (контекст: что за проект, кто в команде). T — Task (твоя задача в этой ситуации). A — Action (что конкретно ты делал). R — Result (что получилось, желательно с цифрами).
- Что такое static?
Принадлежит классу, а не объекту. static-поле общее для всех экземпляров. static-метод можно вызвать без объекта: ClassName.method(), и у него нет this. static-блок выполняется один раз при загрузке класса.
- Что такое Strategy?
Поведенческий паттерн: вынесение алгоритма в отдельный класс за общим интерфейсом. У клиента — поле типа интерфейса, можно подменять реализацию в рантайме. Например, разные алгоритмы сортировки, разные способы расчёта комиссии.
- Что такое Stream API?
Введён в Java 8. Конвейерная обработка коллекций в декларативном стиле. Поток (Stream) — это последовательность элементов с операциями. Не путать с потоком (Thread) — это про многопоточность.
- Что такое String Pool?
Область в heap, где хранятся уникальные строковые литералы. String s = "hi" кладёт в пул, new String("hi") — создаёт новый объект в heap.
- Что такое String Pool? Почему String immutable?
Безопасность, потокобезопасность, кэширование hashCode, работа String Pool.
- Что такое try-with-resources?
Конструкция, которая гарантирует вызов close() у ресурсов, реализующих AutoCloseable. Заменяет try-finally. При исключении в try ресурсы всё равно закроются детерминированно, а исключение из close() становится suppressed.
- Что такое type erasure?
Стирание дженериков во время компиляции. Последствия: нельзя new T(), нельзя instanceof T, нельзя создать массив T[].
- Что такое var (Java 10+)?
Ключевое слово для вывода типа локальной переменной на этапе компиляции. var list = new ArrayList<String>(); — компилятор сам определит, что это ArrayList<String>. Использовать только для локальных переменных с инициализатором, не для полей и параметров.
- Что такое абстрактный класс? Чем отличается от интерфейса?
Абстрактный класс может содержать состояние и реализованные методы, наследуется только один. Интерфейс — контракт, можно реализовать несколько. С Java 8 в интерфейсах появились default-методы.
- Что такое автобоксинг и unboxing? В чём подвох?
Автоматическое преобразование примитива в обёртку и обратно: Integer i = 5; — это int 5 заворачивается в Integer (boxing). int j = i; — обратно (unboxing). Подвох в том, что если обёртка null, при unboxing получишь NullPointerException.
- Что такое автобоксинг и распаковка?
Автоматическое преобразование примитива в обёртку (int → Integer) и обратно. Имеет цену: создание объекта, возможный NullPointerException при распаковке null.
- Что такое функциональный интерфейс?
Интерфейс с РОВНО ОДНИМ абстрактным методом (default и static не считаются). Можно реализовать лямбдой. @FunctionalInterface — необязательная аннотация для compile-time проверки.
- Что такое функциональный интерфейс? Примеры?
Интерфейс с ровно одним абстрактным методом. @FunctionalInterface — опциональная аннотация. Примеры: Function, Predicate, Consumer, Supplier, Runnable, Comparator.
- Что выведет код?
```java
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
Integer c = Integer.valueOf(128);
Integer d = Integer.valueOf(128);
System.out.println(a == b);
System.out.println(c == d);
```
true и false: значения от -128 до 127 обычно берутся из IntegerCache.
- Что сломается в HashMap/HashSet, если equals переопределён, а hashCode оставлен от Object?
Объекты могут считаться равными через equals, но попадать в разные bucket и не находиться при поиске.