java-interview

Собес в Альфа-Банк (Alfa Digital) · Java Middle

Вопросы, задачи и подготовка к live-coding и техническому интервью.

Темы: Java 11+ · Spring Boot · Kafka · PostgreSQL · Docker · Kubernetes · JUnit · Microservices

← Ко всем гайдам · Канал JavaJub в Telegram


1. Про Альфа-Банк и формат собеса

Альфа-Банк — крупнейший частный банк России, №1 в рейтинге работодателей hh.ru (2025). IT-подразделение Alfa Digital разрабатывает мобильный банк, антифрод-системы, платёжные сервисы, кредитные конвейеры и десятки других продуктов. Для Java Middle это означает: микросервисы, высокие нагрузки, промышленный стек. По отзывам кандидатов на DreamJob, собеседование в Альфу длится 1–2 часа и включает live-coding, вопросы по SQL, обсуждение опыта и проектов. Интервьюеры ценят умение рассуждать вслух и объяснять свои решения.

Как устроен процесс

Этап Длительность Что проверяют
Скрининг HR 20–30 мин Мотивация, ЗП, стек, ожидания
Тех-интервью 60–90 мин Java Core + live-coding + SQL +

Spring

Системный дизайн 30–45 мин Архитектура микросервисов, Kafka

Проверка СБ до недели Стандартная для банков (анкета, беседа)

Оффер — ДМС, стоматология, премии KPI, удалёнка

ФИШКА. Ключевая особенность По отзывам кандидатов: в Альфе ценят умение рассуждать вслух и писать код в реальном времени. Это не экзамен по теории — это проверка инженерного мышления. Если не знаешь — скажи «не сталкивался, но предположу…» — честность ценят.

2. Стек по вакансии

На апрель 2026 года Альфа-Банк активно нанимает Java-разработчиков в Alfa Digital. Стек определён по вакансиям на hh.ru и job.alfabank.ru. Проекты: антифрод, кредитный конвейер, Альфа-Онлайн, электронное подписание.

Обязательный минимум

Будет плюсом

ВНИМАНИЕ · Что это значит Стек Альфы заточен под микросервисы и высокие нагрузки. Kafka, Kubernetes и кэширование — не «плюсы», а рабочие инструменты. На собесе спросят: «А что будет, если Kafka-consumer упадёт?» или «Как обеспечить идемпотентность?»

3. Java Core — что точно спросят

По отзывам кандидатов в банковские IT: equals/hashCode + HashMap — абсолютные чемпионы по частоте. Спрашивают все, абсолютно все. Далее идут Stream API, исключения и String Pool.

Базовые вопросы

Задачи «Что выведет?»

В банках любят такие задачи — проверяют понимание, а не заученность. Вот типичные примеры из реальных собесов:

Тест 1. Передача по значению

Integer i = Integer.valueOf(1);
inc(i);
System.out.println(i); // ?

private static void inc(Integer i) { i++; }

Ответ: 1. Java передаёт ссылки по значению. i++ создаёт новый объект Integer(2) и присваивает локальной переменной. Оригинал не меняется. То же самое со String — неизменяемый объект.

Тест 2. Integer cache

Integer i1 = Integer.valueOf(127);
Integer i2 = Integer.valueOf(127);
System.out.println(i1 == i2); // ?

Integer i3 = Integer.valueOf(128);
Integer i4 = Integer.valueOf(128);
System.out.println(i3 == i4); // ?

Ответ: true, false. IntegerCache кэширует значения от -128 до 127. Для 127 — один объект, ссылки совпадают. Для 128 — два разных объекта. Мораль: для объектов всегда equals(), никогда ==.

Тест 3. Stream API — ленивость

List<Integer> nums = List.of(1, 2, 3, 4, 5);
nums.stream()
    .map(x -> { System.out.print(x+" "); return x; })
    .filter(x -> x > 2)
    .map(x -> { System.out.print(x+" "); return x; })
    .toList();

Ответ: 1 2 3 3 4 4 5 5. Стрим обрабатывает элементы вертикально: каждый элемент проходит всю цепочку. Элементы 1 и 2 не проходят filter → печатаются один раз. Элементы 3,4,5 проходят → печатаются дважды.

СОВЕТ. Лайфхак На вопрос про equals/hashCode приведи практический пример: «если положить объект в HashSet и изменить поле в hashCode — объект потеряется, contains() вернёт false». Это показывает понимание, а не заученность.

4. Коллекции

HashMap — абсолютный чемпион. По опыту кандидатов в банки: «обсасывают бедную мапу со всех сторон». Готовься рассказывать устройство, коллизии, treeify threshold, resize, null-ключи.

5. Многопоточность и JMM

Для Middle ждут уверенное понимание synchronized, volatile, happens-before и пулов потоков. JMM — обязательная тема для банков с высоконагруженными системами.

6. Spring и Spring Boot

В вакансии Альфы прямо указано: «знаешь, как работает Spring/Spring Boot под капотом». Поверхностного понимания недостаточно — будут копать в прокси, автоконфигурацию, жизненный цикл бинов.

7. Hibernate и Spring Data JPA

ЛОВУШКА · Про EAGER «Просто поставить EAGER» — неправильный ответ на N+1. EAGER грузит коллекцию ВСЕГДА, даже когда она не нужна — медленнее и съедает память. Правильно: LAZY по умолчанию + FETCH/EntityGraph там, где нужны данные. Золотое правило: все @OneToMany и

@ManyToMany  LAZY.

8. SQL и PostgreSQL

По отзывам кандидатов Альфа-Банка: SQL спрашивают отдельно — пишешь запросы вживую. Готовь JOIN, GROUP BY, HAVING, оконные функции, EXPLAIN ANALYZE.

9. Микросервисы и Kafka

Микросервисная архитектура — основа стека Альфы. Kafka — главный брокер. Готовься объяснять не только «что это», но и «что делать, когда что-то пошло не так».

10. Docker, Kubernetes, CI/CD

11. Тесты: JUnit, Mockito, TDD

В вакансиях Альфы тесты — обязательное требование: JUnit 5, Testcontainers, WireMock. Могут попросить написать тест прямо на собесе.

12. Практические задачи (live-coding)

По отзывам кандидатов: задачи прикладные, без LeetCode Hard. REST-контроллер, unit-тест, code review, Stream API, SQL. Рассуждай вслух!

Задача 1. Найти дубликаты

Дан List. Вернуть Set чисел, встречающихся более одного раза.

public Set<Integer> findDuplicates(List<Integer> list) {
    Set<Integer> seen = new HashSet<>();
    Set<Integer> duplicates = new HashSet<>();
    for (Integer n : list) {
        if (!seen.add(n)) {
            duplicates.add(n);
        }
    }
    return duplicates;
}

O(n) время, O(n) память. seen.add() возвращает false, если элемент уже есть — элегантная проверка. Альтернатива через Stream: Collectors.groupingBy + counting() + filter > 1.

Задача 2. Группировка Stream API

// Отдел → список сотрудников Map<String, List> byDept = employees.stream()

.collect(Collectors.groupingBy(Employee::getDepartment));

// Отдел → количество Map<String, Long> counts = employees.stream() .collect(Collectors.groupingBy(

Employee::getDepartment, Collectors.counting()));

// Отдел → средняя зарплата Map<String, Double> avgSalary = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment,

Collectors.averagingDouble(Employee::getSalary)));

Задача 3. SQL вживую

Отделы со средней зарплатой > 100 000:

SELECT d.name, AVG(e.salary) AS avg_salary
FROM employees e
JOIN departments d ON d.id = e.department_id
GROUP BY d.name
HAVING AVG(e.salary) > 100000
ORDER BY avg_salary DESC;

Индексы: employees.department_id, покрывающий (department_id, salary). Продолжат: сотрудники с зарплатой выше средней по СВОЕМУ отделу → AVG() OVER (PARTITION BY department_id).

Задача 4. SQL из собесов Альфы

Покупатели, купившие Laptop И Monitor в марте 2024:

SELECT c.name
FROM customer c
JOIN purchase p ON c.customer_key = p.customer_key
JOIN product pr ON p.product_key = pr.product_key
WHERE pr.name IN ('Laptop', 'Monitor')
  AND EXTRACT(MONTH FROM p.date) = 3
  AND EXTRACT(YEAR FROM p.date) = 2024
GROUP BY c.customer_key, c.name
HAVING COUNT(DISTINCT pr.name) = 2;

Ключ: HAVING COUNT(DISTINCT pr.name) = 2 — покупатель купил ОБА товара. Без DISTINCT — дубли покупок сломают подсчёт.

Задача 5. REST-контроллер

@RestController
@RequestMapping("/api/users")
public class UserController {

     private final UserService userService;

     public UserController(UserService userService) {
         this.userService = userService;
     }

     @PostMapping
     @ResponseStatus(HttpStatus.CREATED)
     public UserDto create(@RequestBody @Valid CreateUserRequest req) {
         return userService.create(req);
     }

     @GetMapping("/{id}")
     public UserDto getById(@PathVariable Long id) {
         return userService.findById(id);
     }
}

public record CreateUserRequest(
    @NotBlank String name,
    @Email String email,
    @Min(18) int age
) {}

Constructor injection, DTO (не Entity), @Valid, 201 Created для POST, record (Java 16+). Обработка MethodArgumentNotValidException через @RestControllerAdvice.

Задача 6. Unit-тест Mockito + AssertJ

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock private UserRepository userRepository;
    @Mock private EmailSender emailSender;
    @InjectMocks private UserService userService;

     @Test
     void shouldRegisterUserAndSendEmail() {
         // given
         User saved = new User(1L, "Alice", "alice@example.com");
         when(userRepository.save(any(User.class))).thenReturn(saved);
         // when
         User result = userService.register("alice@example.com", "Alice");
         // then
         assertThat(result.getId()).isEqualTo(1L);
         ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
         verify(userRepository).save(captor.capture());
         assertThat(captor.getValue().getName()).isEqualTo("Alice");
         verify(emailSender).sendWelcome("alice@example.com");
    }
}

Задача 7. Потокобезопасный кэш

public class SimpleCache {
    private final Map<String, String> cache = new ConcurrentHashMap<>();

    public String get(String key, Function<String, String> loader) {
        return cache.computeIfAbsent(key, loader);
    }
}

ConcurrentHashMap (не HashMap — бесконечный цикл в Java 7, потеря данных). computeIfAbsent (не containsKey+put — гонка). Ограничение размера: Caffeine/Guava Cache с maxSize и TTL.

Задача 8. Code Review

public class UserCache {
    private static Map<Long, User> cache = new HashMap<>();

    public static User getUser(Long id) {
        if (cache.containsKey(id)) {
            return cache.get(id);
        }
        User user = loadFromDb(id);
        cache.put(id, user);
        return user;
    }
}

Проблемы: 1) HashMap не потокобезопасен → ConcurrentHashMap. 2) containsKey+get → computeIfAbsent. 3) Кэш бесконечный → memory leak → нужен TTL/maxSize. 4) static глобальное состояние → Spring-бин. 5) null от loadFromDb не обработан.

Задача 9. N+1 — найти и починить

@Entity Order с @OneToMany(mappedBy="order") List<OrderItem> items (LAZY). В сервисе: findAll() +
цикл по items.size(). Решения: JOIN FETCH в @Query, @EntityGraph(attributePaths="items"),
@BatchSize(size=100), DTO-проекция. «Просто поставить EAGER»  неправильный ответ.

13. План подготовки + чек-лист

За 2–3 недели

За неделю

В день собеса

ВНИМАНИЕ · Про банковские проекты Альфа-Банк проводит проверку СБ — стандарт для банков. Удалёнка возможна (полная или гибрид). IT-хабы: Москва, Питер, Екатеринбург. Сезонный коворкинг в Сочи.

Финальный чек-лист

Блок Готов, если можешь…
Java Core equals/hashCode на примере + как нарушение

ломает HashSet

Коллекции HashMap в Java 8+: бакеты, treeify, resize, mutable-ключ

Многопоточность Потокобезопасный singleton + volatile в DCL + happens-before

Spring @Transactional под капотом + self-invocation + Propagation

Hibernate N+1: найти, объяснить, 2+ способа починить

SQL JOIN+GROUP BY+HAVING + оконная функция + обосновать индекс

Kafka Consumer Group, гарантии, идемпотентность
Docker/K8s Dockerfile multi-stage + Pod/Deployment/Service +

probes

Тесты Mockito + ArgumentCaptor + AssertJ (given-when-then)

Live-coding 15 мин чистое решение + сложность + edge cases


Удачи на собесе!

// git push origin offer


Гайд из канала JavaJub — свежие разборы собесов выходят там первыми: @java_jub.

← Ко всем гайдам