Библиотека собеса по Java | вопросы с собеседований
6.48K subscribers
410 photos
9 videos
626 links
Вопросы с собеседований по Java и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/08c603b6

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?

Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».

🗓 Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы

🧑🏻‍💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.

🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.

👉 Занять место на вебинаре
✔️ 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥73👍2
Расскажите о паттерне 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1
🗓 14 мая в 19:00 (Мск) встречаемся в онлайне.

Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.

В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.

Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM.
- Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества.
- Ответим на ваши вопросы и разберем кейсы участников.


🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы.

👉 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Расскажите о паттерне 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍21
Расскажите о паттерне 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥1
🔥 Знакомьтесь с экспертом Proglib.academy: Эмиль Сатаев

Эмиль — эксперт с 8-летним опытом в разработке, который специализируется на внедрении LLM и агентных подходов в реальные коммерческие сервисы. Он точно знает, как проектировать архитектуру так, чтобы ИИ-функции работали стабильно в связке с внешними системами.

🏃‍♀️ Уже 14 мая Эмиль проведет открытый вебинар!

Обсудим самую «больную» тему: «Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало».

🗓 Когда: 14 мая в 19:00 (Мск)

Почему Эмиля стоит послушать:

🟣 8+ лет в разработке (Backend и Frontend)
Прошел путь от фулстека до Backend Platform Developer в SMIT.Studio.


🟣 Международный исследовательский опыт
Работал исследователем в Институте ИИ НИУ ВШЭ и в Национальном университете Сингапура (NUS).


🟣 Преподаватель-практик
Ведет семинары в НИУ ВШЭ, в том числе по проектированию и разработке агентских систем.


🟣 Мастер интеграции AI в Backend
Его главная суперсила — умение правильно встраивать LLM через API, выстраивать workflow и агентную логику в сложных распределенных системах.


🔗 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
Расскажите о паттерне Bridge

Bridge — это структурный паттерн, который разделяет абстракцию и реализацию так, чтобы они могли изменяться независимо друг от друга.

Простыми словами: вместо одной толстой иерархии наследования вы разбиваете её на две независимые — «что делать» и «как делать» — и связываете их композицией.

▪️ Пример:

Система отправки уведомлений: типы уведомлений (срочное, обычное) × каналы доставки (email, SMS, push). Без Bridge это 6 классов, с каждым новым каналом — взрывной рост.

// Реализация — «как доставить»
interface MessageSender {
void send(String message, String recipient);
}

class EmailSender implements MessageSender {
public void send(String message, String recipient) {
System.out.println("Email → " + recipient + ": " + message);
}
}

class SmsSender implements MessageSender {
public void send(String message, String recipient) {
System.out.println("SMS → " + recipient + ": " + message);
}
}

// Абстракция — «что отправить»
abstract class Notification {
protected MessageSender sender; // мост к реализации

public Notification(MessageSender sender) {
this.sender = sender;
}

abstract void notify(String recipient, String message);
}

class UrgentNotification extends Notification {
public UrgentNotification(MessageSender sender) {
super(sender);
}

void notify(String recipient, String message) {
sender.send("[СРОЧНО] " + message, recipient);
sender.send("[СРОЧНО] Повторное напоминание: " + message, recipient);
}
}

class RegularNotification extends Notification {
public RegularNotification(MessageSender sender) {
super(sender);
}

void notify(String recipient, String message) {
sender.send(message, recipient);
}
}

// Использование — любая комбинация
Notification urgentSms = new UrgentNotification(new SmsSender());
urgentSms.notify("+79991234567", "Сервер упал");

Notification regularEmail = new RegularNotification(new EmailSender());
regularEmail.notify("dev@company.com", "Деплой завершён");


▪️ Когда использовать

Есть две ортогональные оси изменения (тип × реализация)
Хотите избежать «взрыва» подклассов
Нужно переключать реализацию в runtime

▪️ Bridge vs Strategy

— Strategy: меняет один алгоритм внутри объекта
— Bridge: разделяет целые иерархии абстракции и реализации

▪️ Минус

Усложняет код, если оси изменения всего одна — тогда достаточно обычного полиморфизма.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥1
Расскажите о паттерне Composite

Composite — это структурный паттерн, который позволяет сгруппировать объекты в древовидную структуру и работать с ней так же, как с единичным объектом.

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

▪️ Пример:

Структура отдела в компании: сотрудник и команда состоят в одной иерархии. Нужно посчитать общую зарплату по любой ветке.

// Общий интерфейс
interface OrganizationUnit {
String getName();
long getSalary();
}

// Лист — отдельный сотрудник
class Employee implements OrganizationUnit {
private final String name;
private final long salary;

public Employee(String name, long salary) {
this.name = name;
this.salary = salary;
}

public String getName() { return name; }
public long getSalary() { return salary; }
}

// Композит — отдел, содержит другие элементы
class Department implements OrganizationUnit {
private final String name;
private final List<OrganizationUnit> units = new ArrayList<>();

public Department(String name) {
this.name = name;
}

public void add(OrganizationUnit unit) {
units.add(unit);
}

public String getName() { return name; }

public long getSalary() {
return units.stream()
.mapToLong(OrganizationUnit::getSalary)
.sum();
}
}

// Использование
Department dev = new Department("Разработка");
dev.add(new Employee("Анна", 200_000));
dev.add(new Employee("Борис", 180_000));

Department company = new Department("Компания");
company.add(dev);
company.add(new Employee("CEO Иван", 500_000));

System.out.println(company.getSalary()); // 880000


▪️ Когда использовать

Данные образуют древовидную структуру (файловая система, меню, оргструктура)
Клиентский код должен одинаково работать с простыми и составными объектами

▪️ Минус

Трудно ограничить типы компонентов внутри композита — приходится проверять в runtime.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51🔥1
Расскажите о паттерне Decorator

Decorator — это структурный паттерн, позволяющий на лету расширять поведение объекта, не изменяя его класс. Исходный объект помещается внутрь другого — декоратора, — который перехватывает вызовы и дополняет их нужной функциональностью.

Принцип тот же, что у матрёшки: каждый новый слой добавляет что-то своё.

▪️ Пример:

Сервис обработки данных: базовая загрузка, поверх — логирование, поверх — кэширование. Каждый слой добавляется независимо.

// Базовый интерфейс
interface DataService {
String getData(String key);
}

// Базовая реализация
class DatabaseService implements DataService {
public String getData(String key) {
return "data_from_db_" + key; // запрос в БД
}
}

// Абстрактный декоратор
abstract class DataServiceDecorator implements DataService {
protected final DataService delegate;

public DataServiceDecorator(DataService delegate) {
this.delegate = delegate;
}
}

// Декоратор логирования
class LoggingDecorator extends DataServiceDecorator {
public LoggingDecorator(DataService delegate) {
super(delegate);
}

public String getData(String key) {
System.out.println("LOG: запрос ключа " + key);
String result = delegate.getData(key);
System.out.println("LOG: получен результат");
return result;
}
}

// Декоратор кэширования
class CachingDecorator extends DataServiceDecorator {
private final Map<String, String> cache = new HashMap<>();

public CachingDecorator(DataService delegate) {
super(delegate);
}

public String getData(String key) {
return cache.computeIfAbsent(key, delegate::getData);
}
}

// Использование — собираем как конструктор
DataService service = new CachingDecorator(
new LoggingDecorator(
new DatabaseService()
)
);

service.getData("user:42"); // лог + БД + кэш
service.getData("user:42"); // кэш (без лога и БД)


▪️ Когда использовать

Нужно добавить поведение объекту без наследования
Комбинации поведений непредсказуемы (логирование + кэш + метрики в любом порядке)
Классический пример в JDK: InputStream → BufferedInputStream → GZIPInputStream

▪️ Decorator vs наследование

— Наследование: статическое, одно на класс
— Decorator: динамическое, можно комбинировать

▪️ Минус

Много мелких классов; отладка стека из нескольких обёрток может быть неудобной.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3🔥1
✔️ Java-тест: объект в HashSet есть, но contains() возвращает false

Добавили объект в Set. Он там. Но найти его невозможно. Утечка памяти в проде. Почему?

📦 Задание — code review

Команда хранит активные сессии в HashSet. После смены роли пользователя сессия «пропадает» из множества — contains() возвращает false, remove() не удаляет. Set растёт, память течёт.
public class UserSession {
private Long userId;
private String role;

public UserSession(Long userId, String role) {
this.userId = userId;
this.role = role;
}

// getters, setters

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserSession that = (UserSession) o;
return Objects.equals(userId, that.userId)
&& Objects.equals(role, that.role);
}

@Override
public int hashCode() {
return Objects.hash(userId, role);
}
}

@Service
public class SessionManager {

private final Set<UserSession> activeSessions = new HashSet<>();

public void addSession(UserSession session) {
activeSessions.add(session);
}

public void promoteToAdmin(Long userId) {
activeSessions.stream()
.filter(s -> s.getUserId().equals(userId))
.findFirst()
.ifPresent(s -> s.setRole("ADMIN"));
}

public boolean isActive(UserSession session) {
return activeSessions.contains(session);
}

public void removeSession(UserSession session) {
activeSessions.remove(session);
}
}


// Сценарий бага:
sessionManager.addSession(new UserSession(1L, "USER")); // ОК
sessionManager.promoteToAdmin(1L); // меняем роль
sessionManager.isActive(new UserSession(1L, "ADMIN")); // false ?!
// объект внутри Set есть, но достать его нельзя


▪️ Почему после promoteToAdmin() сессия становится невидимой для contains() и remove()?
▪️ Как исправить, не ломая бизнес-логику?

Ставьте → 🔥, если нравится формат. Если нет → 🌚

💬 Решения под спойлер. Сравним, какое будет лучше.

🐸 Библиотека собеса по Java

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍31👏1
💬 Обратная связь

Как часто вы проходите собеседования?

🔥 — Сейчас активно ищу работу
👍🏼 — Раз в несколько месяцев
❤️ — Раз в полгода-год
😁 — Не прохожу, уже работаю/ещё учусь

🐸 Библиотека собеса по Java
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12😁10👍21