Библиотека собеса по Java | вопросы с собеседований
Открыть в Telegram
Вопросы с собеседований по Java и ответы на них. По рекламе: @tproger_sales_bot Учиться у нас: clc.to/3wECtA Для обратной связи: @proglibrary_feeedback_bot
Больше6 378
Подписчики
-624 часа
-197 дней
-5130 дней
Архив постов
❓ Расскажите о паттерне Builder
Builder — это порождающий паттерн, который позволяет создавать сложные объекты пошагово, разделяя процесс конструирования и представление.
Простыми словами: вместо конструктора с десятком параметров, вы собираете объект по частям, вызывая понятные методы.
▪️ Пример:
HTTP-клиент с множеством необязательных настроек: таймауты, заголовки, ретраи, прокси. Конструктор с 10 параметрами нечитаем, а Builder решает это.
public class HttpClient {
private final String baseUrl;
private final int connectTimeout;
private final int readTimeout;
private final Map<String, String> headers;
private final int maxRetries;
private HttpClient(Builder builder) {
this.baseUrl = builder.baseUrl;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.headers = builder.headers;
this.maxRetries = builder.maxRetries;
}
public static class Builder {
private final String baseUrl; // обязательный
private int connectTimeout = 5000; // значение по умолчанию
private int readTimeout = 10000;
private Map<String, String> headers = new HashMap<>();
private int maxRetries = 3;
public Builder(String baseUrl) {
this.baseUrl = baseUrl;
}
public Builder connectTimeout(int ms) {
this.connectTimeout = ms;
return this;
}
public Builder readTimeout(int ms) {
this.readTimeout = ms;
return this;
}
public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder maxRetries(int retries) {
this.maxRetries = retries;
return this;
}
public HttpClient build() {
return new HttpClient(this);
}
}
}
// Использование
HttpClient client = new HttpClient.Builder("https://api.example.com")
.connectTimeout(3000)
.header("Authorization", "Bearer token")
.maxRetries(5)
.build();
▪️ Когда использовать
— Объект имеет множество параметров (особенно необязательных)
— Нужна immutable-объект с удобным созданием
— Хотите избежать telescoping constructor (конструктор в конструкторе)
▪️ Builder vs конструктор
— Конструктор: подходит, если параметров 2–3 и все обязательные
— Builder: если параметров больше 4 или есть необязательные
▪️ Минус
Дублирование полей между классом и билдером. Lombok @Builder решает это автоматически.
🐸 Библиотека собеса по Java
#patterns❓ Расскажите о паттерне Flyweight
Flyweight — это структурный паттерн, который позволяет вместить большее количество объектов в отведённую оперативную память за счёт разделения общего состояния между объектами.
Простыми словами: вместо создания тысяч похожих объектов, вы создаёте несколько объектов-легковесов с общими данными и передаёте уникальные данные извне при использовании.
▪️ Пример
Текстовый редактор, где каждая буква — это объект. Если создавать отдельный объект для каждой буквы с полным набором свойств (шрифт, размер, цвет), миллион символов займут гигабайты памяти.
// Flyweight — легковес с общим состоянием class CharacterStyle { private final String font; // внутреннее состояние private final int size; // (intrinsic) private final String color; public CharacterStyle(String font, int size, String color) { this.font = font; this.size = size; this.color = color; } public void render(char character, int x, int y) { // внешнее состояние System.out.println("Рендер '" + character + "' в позиции (" + x + "," + y + ")"); } } // Фабрика для переиспользования flyweight-объектов class StyleFactory { private Map<String, CharacterStyle> styles = new HashMap<>(); public CharacterStyle getStyle(String font, int size, String color) { String key = font + size + color; return styles.computeIfAbsent(key, k -> new CharacterStyle(font, size, color)); } } // Использование StyleFactory factory = new StyleFactory(); // Миллион символов используют всего несколько объектов стилей CharacterStyle arial12 = factory.getStyle("Arial", 12, "black"); arial12.render('H', 0, 0); arial12.render('e', 10, 0); arial12.render('l', 20, 0); arial12.render('l', 30, 0); arial12.render('o', 40, 0);▪️ Когда использовать — Приложение создаёт огромное количество однотипных объектов — Объекты потребляют много памяти — Большую часть состояния можно вынести за пределы объекта — Приложение не зависит от идентичности объектов (можно переиспользовать) ▪️ Минусы — Усложнение кода из-за разделения состояния — Flyweight должен быть immutable для безопасного переиспользования 🐸 Библиотека собеса по Java #patterns
🗓 14 мая в 19:00 (Мск) встречаемся в онлайне.
Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.
В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.
Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM. - Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества. - Ответим на ваши вопросы и разберем кейсы участников.🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы. 👉 Зарегистрироваться на вебинар
❓ Расскажите о паттерне Prototype
Prototype — это порождающий паттерн, который позволяет копировать объекты, не вдаваясь в подробности их реализации.
Простыми словами: вместо создания объекта с нуля через конструктор, вы клонируете уже существующий экземпляр.
▪️ Пример:
Система отчетов, где базовая конфигурация отчета сложная (подключение к БД, загрузка шаблонов, настройка форматирования). Вместо каждый раз создавать отчет заново, клонируем прототип.
// Прототип abstract class Report implements Cloneable { protected String template; protected DatabaseConnection dbConnection; protected Map<String, Object> settings; // Сложная инициализация public Report() { this.dbConnection = new DatabaseConnection(); // затратная операция this.settings = loadDefaultSettings(); // загрузка из файла } @Override public Report clone() { try { Report cloned = (Report) super.clone(); // Глубокое копирование для изменяемых полей cloned.settings = new HashMap<>(this.settings); return cloned; } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } abstract void generate(); } class SalesReport extends Report { void generate() { System.out.println("Генерация отчета по продажам"); } } // Использование Report prototype = new SalesReport(); // долгая инициализация // Быстрое создание копий Report report1 = prototype.clone(); Report report2 = prototype.clone(); report2.settings.put("period", "Q2"); // изменяем только нужные параметры▪️ Когда использовать — Создание объекта затратно (сложная инициализация, обращение к БД, парсинг файлов) — Нужно избежать привязки к конкретным классам при создании копий — Объекты различаются только состоянием, а не поведением ▪️ Важно: Shallow vs Deep Copy Shallow copy — копируются только примитивы, ссылки на объекты остаются теми же Deep copy — создаются копии вложенных объектов ▪️ Альтернативы clone() — Copy constructor: new Report(original) — Статический метод: Report.copy(original) — Сериализация: для очень сложных объектов ▪️ Минус Клонирование объектов с циклическими ссылками может быть сложным. 🐸 Библиотека собеса по Java #patterns
✔️ Java-тест: транзакция, которая не откатывается
@Transactional стоит, исключение летит. Данные в БД остаются. Почему?
📦 Задание — code review
Команда добавила транзакцию на метод сохранения заказа. При ошибке валидации данные всё равно коммитятся в базу. @Transactional на месте, но не работает.
public class CheckedBusinessException extends Exception {
public CheckedBusinessException(String message) {
super(message);
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
@Transactional
public void placeOrder(Order order) throws CheckedBusinessException {
orderRepository.save(order);
inventoryService.reserveStock(order);
}
}
@Service
public class InventoryService {
public void reserveStock(Order order) throws CheckedBusinessException {
if (order.getQuantity() > availableStock) {
throw new CheckedBusinessException("Not enough stock");
}
}
}
▪️ Почему @Transactional не откатывает изменения при CheckedBusinessException?
▪️ Как добиться rollback в этом коде
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?
Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».
🗓 Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы
🧑🏻💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.
🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.
👉 Занять место на вебинаре
❓ Расскажите о паттерне Abstract Factory
Abstract Factory — это порождающий паттерн, который создает семейства связанных объектов без привязки к конкретным классам.
Простыми словами: вы создаете не один объект, а целый набор совместимых между собой объектов.
▪️ Пример:
Система уведомлений, которая работает с разными провайдерами (AWS, Firebase). Для каждого провайдера нужны свои клиенты для отправки email, SMS и push-уведомлений.
// Абстрактные продукты interface EmailSender { void send(String to, String message); } interface SmsSender { void send(String phone, String message); } // Абстрактная фабрика interface NotificationFactory { EmailSender createEmailSender(); SmsSender createSmsSender(); } // AWS реализация class AwsEmailSender implements EmailSender { public void send(String to, String message) { System.out.println("Отправка через AWS SES: " + to); } } class AwsSmsSender implements SmsSender { public void send(String phone, String message) { System.out.println("Отправка через AWS SNS: " + phone); } } class AwsNotificationFactory implements NotificationFactory { public EmailSender createEmailSender() { return new AwsEmailSender(); } public SmsSender createSmsSender() { return new AwsSmsSender(); } } // Аналогично FirebaseNotificationFactory... // Использование NotificationFactory factory = new AwsNotificationFactory(); EmailSender email = factory.createEmailSender(); SmsSender sms = factory.createSmsSender(); // Гарантия: оба сервиса работают через AWS▪️ В чем отличие от Factory Method — Factory Method создает один продукт — Abstract Factory создает семейство продуктов (email + sms + push) ▪️ Когда использовать Когда нужны наборы связанных объектов, которые должны работать вместе (клиенты для разных облаков, драйверы БД, парсеры форматов). ▪️ Минус Сложно добавить новый тип продукта — придется менять все фабрики. 🐸 Библиотека собеса по Java #patterns
❓ Расскажите о паттерне Factory Method
Factory Method — порождающий паттерн, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
🔹 Когда использовать:
Когда заранее неизвестны типы и зависимости объектов, или когда нужно делегировать создание объектов подклассам.
🔹 Как работает:
Создается абстрактный метод для создания объектов, а конкретные подклассы переопределяют его, возвращая нужные типы.
🔹 Плюсы:
— избавляет от привязки к конкретным классам;
— упрощает добавление новых типов продуктов;
— следует Open/Closed Principle.
🐸 Библиотека собеса по Java
#patterns
❓ Расскажите о паттерне Adapter
Adapter (Адаптер) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли "переходника" между двумя интерфейсами.
🔹 Когда использовать:
Когда нужно использовать существующий класс, но его интерфейс не соответствует требуемому. Например, при интеграции сторонних библиотек или работе с легаси-кодом.
🐸 Библиотека собеса по Java
#patterns
❓ Расскажите о паттерне Singleton?
Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса существует только один экземпляр, и предоставляет глобальную точку доступа к нему.
🔹 Основные характеристики:
— приватный конструктор запрещает создание экземпляров извне;
— статический метод getInstance() возвращает единственный экземпляр;
— статическое поле для хранения экземпляра.
🔹 Когда использовать:
— объект содержит глобальное состояние;
— создание объекта ресурсозатратно;
— нужен единый координатор действий;
— логически должен быть один экземпляр.
🐸 Библиотека собеса по Java
#patterns
❓ Расскажите о паттерне Strategy?
Strategy — это поведенческий паттерн, который позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми.
Когда использовать:
— есть несколько способов выполнения одной операции;
— нужно избежать множественных if-else или switch;
— алгоритмы должны выбираться в runtime.
Преимущества: соблюдение Open/Closed Principle, устранение условных операторов, гибкость выбора алгоритма.
🐸 Библиотека собеса по Java
#patterns
❓ Как работает HashMap внутри? Что происходит при коллизии и при достижении load factor?
HashMap — массив бакетов (Node[] table). Ключ хешируется, хеш определяет индекс бакета: index = hash & (capacity - 1
Что происходит при put(key, value):
Считается hash(key) — не просто hashCode(), а с дополнительным перемешиванием старших битов ((h = key.hashCode()) ^ (h >>> 16)). Это снижает коллизии при маленьком размере таблицы.
Находится бакет по индексу. Если пустой — кладём первым. Если нет — коллизия.
🔹 Коллизия:
До Java 8 → связный список в бакете. Поиск O(n) в худшем случае.
С Java 8 → когда в бакете больше 8 элементов и capacity >= 64, список превращается в красно-чёрное дерево. Поиск становится O(log n). Обратно в список — при сжатии ниже 6 элементов.
// Упрощённо: Node в списке или TreeNode в дереве
static class Node<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
🔹 Load factor и resize:
По умолчанию capacity = 16, loadFactor = 0.75. Порог = capacity * loadFactor = 12.
Как только элементов стало больше 12 — начинается resize(): таблица удваивается до 32, все элементы перераспределяются по новым бакетам. Это O(n) операция.
Поэтому если заранее знаете размер — задавайте начальную ёмкость:
// Хотим 1000 элементов без resize:
// 1000 / 0.75 ≈ 1334, берём следующую степень двойки
Map<String, Integer> map = new HashMap<>(2048);
🐸 Библиотека собеса по Java
#core❓ Как работает HashMap внутри? Что происходит при коллизии и при достижении load factor?
HashMap — массив бакетов (Node[] table). Ключ хешируется, хеш определяет индекс бакета: index = hash & (capacity - 1
Что происходит при put(key, value):
Считается hash(key) — не просто hashCode(), а с дополнительным перемешиванием старших битов ((h = key.hashCode()) ^ (h >>> 16)). Это снижает коллизии при маленьком размере таблицы.
Находится бакет по индексу. Если пустой — кладём первым. Если нет — коллизия.
🔹 Коллизия:
До Java 8 → связный список в бакете. Поиск O(n) в худшем случае.
С Java 8 → когда в бакете больше 8 элементов и capacity >= 64, список превращается в красно-чёрное дерево. Поиск становится O(log n). Обратно в список — при сжатии ниже 6 элементов.
// Упрощённо: Node в списке или TreeNode в дереве
static class Node<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
🔹 Load factor и resize:
По умолчанию capacity = 16, loadFactor = 0.75. Порог = capacity * loadFactor = 12.
Как только элементов стало больше 12 — начинается resize(): таблица удваивается до 32, все элементы перераспределяются по новым бакетам. Это O(n) операция.
Поэтому если заранее знаете размер — задавайте начальную ёмкость:
// Хотим 1000 элементов без resize:
// 1000 / 0.75 ≈ 1334, берём следующую степень двойки
Map<String, Integer> map = new HashMap<>(2048);
🐸 Библиотека собеса по Java
#core✔️ Java-тест: кэш, который врёт
Метод возвращает устаревшие данные. Иногда и только на нескольких потоках. Воспроизвести локально — нереально 👇
📦 Задание — code review
Команда добавила простой in-memory кэш для тяжёлых вычислений. На одном потоке работает идеально. На проде с нагрузкой — иногда возвращает старый результат или null.
@Component
public class PricingCache {
private final Map<String, BigDecimal> cache = new HashMap<>();
private final PricingEngine pricingEngine;
public PricingCache(PricingEngine pricingEngine) {
this.pricingEngine = pricingEngine;
}
public BigDecimal getPrice(String productId) {
if (cache.containsKey(productId)) {
return cache.get(productId);
}
BigDecimal price = pricingEngine.calculate(productId);
cache.put(productId, price);
return price;
}
public void invalidate(String productId) {
cache.remove(productId);
}
public void invalidateAll() {
cache.clear();
}
}
▪️ Объясни
— В чём проблема в коде
— Как переписать getPrice верно
Ставьте → 🔥, если нравится формат. Если нет → 🌚
💬 Решения под спойлер. Сравним, какое будет лучше.
🐸 Библиотека собеса по Java
#practise❓ Чем отличается ReentrantLock от synchronized?
🔹 synchronized — встроенный монитор JVM. Прост, но ограничен:
— нельзя прервать ожидание
— нет tryLock
— нельзя узнать, занят ли монитор
🔹 ReentrantLock из java.util.concurrent.locks — явная блокировка с расширенными возможностями:
ReentrantLock lock = new ReentrantLock();
// Попытка без ожидания
if (lock.tryLock()) {
try {
// критическая секция
} finally {
lock.unlock(); // обязателен!
}
}
// С таймаутом
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) { ... }
// С прерыванием
lock.lockInterruptibly();
🔹 Когда synchronized
Простые сценарии, нет нужды в доп. функциях, меньше кода и риска ошибок.
🔹 Когда ReentrantLock
Нужен tryLock, таймаут, прерывание, несколько Condition, или fairness.
🐸 Библиотека собеса по Java
#concurrency❓ Для чего нужен @Conditional в Spring?
@Conditional регистрирует бин только при выполнении определённого условия.
@Bean
@Conditional(WindowsCondition.class)
public Service windowsService() {
return new WindowsService();
}
class WindowsCondition implements Condition {
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
return System.getProperty("os.name")
.contains("Windows");
}
}
Spring Boot предоставляет готовые: @ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnMissingBean.
🐸 Библиотека собеса по Java
#springОсталось всего 4 места на курс по ИИ-агентам. 30 апреля закрываем набор окончательно.
В ГС честно рассказали:
— Кому курс не подойдет;
— Какой хардкор в программе (LangGraph, AutoGen, CrewAI);
— Как мы даем токены, чтобы вы не тратили свои деньги.
🏃♀️ Записаться, пока есть места
❓ В чем разница между параллелизмом и многопоточностью?
Многопоточность — это способность программы выполнять несколько потоков , где каждый поток может выполняться независимо, но не обязательно одновременно. Потоки могут переключаться между собой, создавая иллюзию одновременности, однако они могут работать на одном ядре процессора.
Параллелизм, с другой стороны, — это выполнение нескольких задач одновременно, обычно на разных процессорах или ядрах. В контексте параллелизма, задачи действительно выполняются одновременно.
🐸 Библиотека собеса по Java
#concurrency
❓ Что такое иммутабельный объект?
Иммутабельный объект — это объект, состояние которого невозможно изменить после его создания. Иммутабельность способствует безопасности потоков и предотвращению ошибок, связанных с изменениями состояния объектов. Примером может служить класс String.
🐸 Библиотека собеса по Java
#core
