Please open Telegram to view this post
VIEW IN TELEGRAM
😁27👍5
Forwarded from Библиотека собеса по Java | вопросы с собеседований
Напишите метод для production-кода 👇
📦 Задание
Реализуйте метод, который группирует строки по их длине:
public Map<Integer, List<String>> groupByLength(List<String> strings) {}Требования
— Обработать null и пустой список → вернуть пустую Map
— Игнорировать null-элементы в списке
— Порядок строк в группах сохраняется
— Пустые строки группировать с ключом 0
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍2🥱2❤1
Делитесь фотографиями выходных.
До Балкан тоже начинает потихоньку добираться осень 🍁
Отправляйте фото в комментарии👇🏻
#DevLife
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
😍10🔥5❤1👍1
Когда рефакторите legacy-код или чистите архитектуру — приходится делать десятки преобразований. А вы знали, что в IDEA есть универсальное меню для всех рефакторингов?
🔹 Что делает
— Открывает контекстное меню со всеми доступными рефакторингами для текущего элемента
— Автоматически фильтрует опции в зависимости от контекста (переменная, метод, класс)
— Позволяет выбрать нужную операцию по цифре или первой букве
🔹 Зачем это нужно
— Не нужно помнить десятки отдельных хоткеев — один хоткей открывает все варианты
— Молниеносный доступ к редким, но мощным рефакторингам (Extract Parameter Object, Introduce Parameter, Pull Members Up)
— Безопасно переименовывает, перемещает и трансформирует код с учётом всех зависимостей
— Особенно полезно для сложных преобразований, которые вручную делать долго и опасно
🔹 Как использовать
— Поставьте курсор на переменную, метод, класс или любой элемент кода
— Нажмите
Ctrl+Alt+Shift+T (Windows/Linux) или ⌃+T (macOS)— Выберите нужный рефакторинг из списка: Extract Method, Rename, Change Signature, Inline и другие
— Настройте параметры → Enter → готово. IDEA сама обновит все использования
#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤5🔥4
Please open Telegram to view this post
VIEW IN TELEGRAM
😁31👍8🔥4
🔥 Как подключить MapStruct к проекту
MapStruct — это code-generation библиотека для автоматического маппинга между Java-объектами.
Работает на этапе компиляции, генерирует чистый код без рефлексии — в разы быстрее ModelMapper и Dozer.
1️⃣ Добавляем зависимости
Нужно добавить две зависимости: саму библиотеку mapstruct и процессор mapstruct-processor. Процессор отвечает за генерацию кода во время компиляции.
Критически важно правильно настроить maven-compiler-plugin, именно он запускает annotation processors. В секции annotationProcessorPaths прописываем mapstruct-processor, а если используете Lombok, добавляем его тоже плюс специальный binding для их совместной работы.
2️⃣ Создаём Mapper интерфейс
Создаёте обычный Java-интерфейс с аннотацией @Mapper(componentModel = "spring"). Параметр componentModel указывает, что MapStruct должен сгенерировать Spring-бин, который можно инжектить через конструктор.
В интерфейсе объявляете методы-конвертеры: например UserDto toDto(User user) или List<UserDto> toDtoList(List<User> users). MapStruct сам поймёт, какие поля во что маппить, если названия совпадают.
3️⃣ Настраиваем кастомный маппинг
Когда названия полей отличаются или нужна дополнительная логика, используйте аннотацию @Mapping:
▪️ source/target — для переименования полей: @Mapping(target = "fullName", source = "firstName")
▪️ expression — для Java-выражений: можно написать простую логику прямо в аннотации
▪️ ignore — чтобы игнорировать поле: @Mapping(target = "password", ignore = true)
▪️ dateFormat — для форматирования дат: автоматическая конвертация LocalDateTime в String
▪️ qualifiedByName — для вызова кастомных методов-конвертеров
4️⃣ Работа со вложенными объектами
MapStruct умеет автоматически маппить вложенные структуры. Если у вас в User есть поле Address, а в UserDto есть AddressDto — создайте отдельный AddressMapper и укажите его в параметре uses = {AddressMapper.class}.
MapStruct сам найдёт нужный маппер и применит его для вложенных объектов. Это работает рекурсивно для любой глубины вложенности.
5️⃣ Обновление существующих объектов
Часто нужно не создавать новый объект, а обновить существующий. Для этого используйте аннотацию @MappingTarget на втором параметре метода. MapStruct сгенерирует код, который обновит только нужные поля.
Добавьте @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) — тогда null-значения из DTO не затрут существующие данные в Entity.
6️⃣ Кастомные конвертеры
Для сложной логики добавьте в интерфейс default-методы. Пометьте их @Named("имя") и ссылайтесь через qualifiedByName. Например, для конвертации enum в русский текст или склейки нескольких полей.
Можно также создать отдельный класс с @Component и helper-методами, затем подключить его через параметр uses.
✔️ Что происходит под капотом
MapStruct анализирует ваш интерфейс на этапе компиляции, смотрит на типы полей, их названия и аннотации. Затем генерирует простой Java-код с прямым присвоением значений, никакой магии, рефлексии или proxy.
Сгенерированный код можно открыть и прочитать. Он выглядит так, будто вы написали его вручную. Это упрощает отладку и понимание происходящего.
🐸 Библиотека джависта
#Enterprise
MapStruct — это code-generation библиотека для автоматического маппинга между Java-объектами.
Работает на этапе компиляции, генерирует чистый код без рефлексии — в разы быстрее ModelMapper и Dozer.
Нужно добавить две зависимости: саму библиотеку mapstruct и процессор mapstruct-processor. Процессор отвечает за генерацию кода во время компиляции.
Критически важно правильно настроить maven-compiler-plugin, именно он запускает annotation processors. В секции annotationProcessorPaths прописываем mapstruct-processor, а если используете Lombok, добавляем его тоже плюс специальный binding для их совместной работы.
Создаёте обычный Java-интерфейс с аннотацией @Mapper(componentModel = "spring"). Параметр componentModel указывает, что MapStruct должен сгенерировать Spring-бин, который можно инжектить через конструктор.
В интерфейсе объявляете методы-конвертеры: например UserDto toDto(User user) или List<UserDto> toDtoList(List<User> users). MapStruct сам поймёт, какие поля во что маппить, если названия совпадают.
Когда названия полей отличаются или нужна дополнительная логика, используйте аннотацию @Mapping:
▪️ source/target — для переименования полей: @Mapping(target = "fullName", source = "firstName")
▪️ expression — для Java-выражений: можно написать простую логику прямо в аннотации
▪️ ignore — чтобы игнорировать поле: @Mapping(target = "password", ignore = true)
▪️ dateFormat — для форматирования дат: автоматическая конвертация LocalDateTime в String
▪️ qualifiedByName — для вызова кастомных методов-конвертеров
MapStruct умеет автоматически маппить вложенные структуры. Если у вас в User есть поле Address, а в UserDto есть AddressDto — создайте отдельный AddressMapper и укажите его в параметре uses = {AddressMapper.class}.
MapStruct сам найдёт нужный маппер и применит его для вложенных объектов. Это работает рекурсивно для любой глубины вложенности.
Часто нужно не создавать новый объект, а обновить существующий. Для этого используйте аннотацию @MappingTarget на втором параметре метода. MapStruct сгенерирует код, который обновит только нужные поля.
Добавьте @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) — тогда null-значения из DTO не затрут существующие данные в Entity.
Для сложной логики добавьте в интерфейс default-методы. Пометьте их @Named("имя") и ссылайтесь через qualifiedByName. Например, для конвертации enum в русский текст или склейки нескольких полей.
Можно также создать отдельный класс с @Component и helper-методами, затем подключить его через параметр uses.
MapStruct анализирует ваш интерфейс на этапе компиляции, смотрит на типы полей, их названия и аннотации. Затем генерирует простой Java-код с прямым присвоением значений, никакой магии, рефлексии или proxy.
Сгенерированный код можно открыть и прочитать. Он выглядит так, будто вы написали его вручную. Это упрощает отладку и понимание происходящего.
#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥2🥱1
LinkedHashSet — это реализация интерфейса Set из пакета java.util, которая сохраняет порядок вставки элементов. Это гибрид между HashSet (быстрый поиск) и списком (предсказуемый порядок итерации).
📦 Базовая структура
LinkedHashSet — это тонкая обёртка над LinkedHashMap. Внутри:
— Все элементы хранятся как ключи в LinkedHashMap.
— Значения — константа PRESENT (заглушка Object).
— Порядок поддерживается через двусвязный список узлов.
Главная особенность:
— O(1) для add, remove, contains (как у HashSet).
— Предсказуемый порядок итерации (порядок вставки).
— Немного больше памяти, чем HashSet (~25% overhead на связи).
🔍 Как устроено хранение
LinkedHashSet полностью делегирует работу LinkedHashMap, а тот устроен так:
Entry<K,V> — узел хранения:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before; // предыдущий в порядке вставки
Entry<K,V> after; // следующий в порядке вставки
}
```
### **Двойная структура:**
1. **Хэш-таблица** (массив бакетов):
- Быстрый доступ по хэшу → O(1).
- Разрешение коллизий через цепочки/деревья.
2. **Двусвязный список**:
- Связывает все элементы в порядке добавления.
- Голова списка: `head` (первый добавленный).
- Хвост списка: `tail` (последний добавленный).
**Визуализация:**
```
Хэш-таблица:
Bucket[0]: null
Bucket[1]: Entry("B") ----→ Entry("F")
Bucket[2]: Entry("A")
Bucket[3]: Entry("C")
Двусвязный список (порядок вставки):
head → Entry("A") ⇄ Entry("B") ⇄ Entry("C") ⇄ Entry("F") ← tail
➕ add(E element) — добавление
1. Вычисляется хэш элемента: hash(e).
2. Определяется индекс бакета: index = hash & (n-1).
3. Проверяется наличие элемента в бакете:
— если есть → возвращается false (дубликат).
— если нет → создаётся новый Entry.
4. Новый Entry:
— добавляется в бакет хэш-таблицы.
— связывается с tail в двусвязном списке.
— обновляется tail = newEntry.
5. При необходимости таблица расширяется (load factor > 0.75).
Сложность: O(1) в среднем.
🔎 contains(Object o) — проверка наличия
1. Вычисляется хэш объекта.
2. Проверяется соответствующий бакет.
3. Сравнивается через equals().
4. Двусвязный список НЕ используется для поиска.
Сложность: O(1) в среднем.
➖ remove(Object o) — удаление
1. Находится Entry в хэш-таблице по хэшу.
2. Узел удаляется из бакета.
3. Узел отсоединяется от двусвязного списка.
4. Обновляются ссылки head/tail при необходимости.
Сложность: O(1) в среднем.
⚖️ Важные нюансы
1. Наследование от HashSet
Наследует поведение HashSet, но меняет внутреннюю реализацию. Конструкторы создают LinkedHashMap вместо HashMap.
2. Null элементы
Один null может быть добавлен (как в HashSet).
3. Не потокобезопасен
Для многопоточного доступа требуется внешняя синхронизация. Альтернатива: CopyOnWriteArraySet (но без хэш-таблицы).
4. Equals и hashCode
Сравнивает содержимое, игнорируя порядок:
5. Capacity и Load Factor
Начальные значения: Capacity 100, load factor 0.75
Начальная ёмкость должна учитывать ожидаемый размер.
При достижении threshold (capacity × load factor) происходит resize.
1. Порядок не важен
Используйте HashSet — проще и немного быстрее (меньше overhead).
2. Нужна сортировка
Используйте TreeSet — автоматическая сортировка по Comparator/Comparable.
3. Многопоточный доступ
Используйте ConcurrentHashMap.newKeySet() или CopyOnWriteArraySet. Или оборачивайте: Collections.synchronizedSet().
4. Критична минимизация памяти:
HashSet использует меньше памяти (~20% экономии).
🔗 Документация: JavaDoc (Java 17)
Ставьте 🔥, если хотите разбор TreeSet!
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍4❤1🎉1
Forwarded from Библиотека задач по Java | тесты, код, задания
Что произойдёт при запуске приложения с таким классом?
Anonymous Quiz
26%
Не запустится, так как нельзя использовать обе эти аннотации на одном классе
44%
Запустится, будет зарегистрирован один бин
7%
Запустится, но Spring не создаст ни одного бина из-за конфликта
8%
Запустится, будет зарегистрировано два разных бина: один с @Component, другой с @Service
14%
Посмотреть ответ
🔥5👍3❤1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁29💯5🔥1👾1
⚙️ Byte Buddy
Byte Buddy — это библиотека для runtime генерации и модификации байт-кода Java. В отличие от классических ASM или Javassist, она предлагает type-safe API и не требует глубоких знаний JVM internals.
📌 Что умеет Byte Buddy
— Создаёт классы и прокси на лету без написания байт-кода вручную
— Перехватывает методы, добавляет логирование, метрики или трейсинг без изменения исходного кода
— Работает как Java-агент для инструментации существующих классов (например, для APM-систем)
— Интегрируется с Mockito, Hibernate и другими фреймворками под капотом
— Поддерживает все современные Java-фичи (records, sealed classes, pattern matching)
🧠 Особенно полезно для
Создания динамических прокси с кастомным поведением, написания собственных фреймворков или библиотек с AOP-логикой, инструментации приложений для мониторинга (как делают Datadog, New Relic), тестирования с подменой реализаций классов.
Byte Buddy даёт мощь низкоуровневой манипуляции классами, но с человеческим API. Если вы когда-то смотрели на ASM и думали "это слишком сложно" — попробуйте Byte Buddy.
🔗 Byte Buddy на GitHub
🐸 Библиотека джависта
#Enterprise
Byte Buddy — это библиотека для runtime генерации и модификации байт-кода Java. В отличие от классических ASM или Javassist, она предлагает type-safe API и не требует глубоких знаний JVM internals.
— Создаёт классы и прокси на лету без написания байт-кода вручную
— Перехватывает методы, добавляет логирование, метрики или трейсинг без изменения исходного кода
— Работает как Java-агент для инструментации существующих классов (например, для APM-систем)
— Интегрируется с Mockito, Hibernate и другими фреймворками под капотом
— Поддерживает все современные Java-фичи (records, sealed classes, pattern matching)
🧠 Особенно полезно для
Создания динамических прокси с кастомным поведением, написания собственных фреймворков или библиотек с AOP-логикой, инструментации приложений для мониторинга (как делают Datadog, New Relic), тестирования с подменой реализаций классов.
Byte Buddy даёт мощь низкоуровневой манипуляции классами, но с человеческим API. Если вы когда-то смотрели на ASM и думали "это слишком сложно" — попробуйте Byte Buddy.
🔗 Byte Buddy на GitHub
#Enterprise
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2🔥1🤔1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁29👾3👍2🤔1
Forwarded from Библиотека собеса по Java | вопросы с собеседований
— StackOverflowError
Возникает когда переполняется
— OutOfMemoryError
Возникает когда J
#jvm
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥2👏1
🔍 Просто о сложном: Sealed Classes
В Java 17 появились Sealed Classes — механизм явного контроля иерархии наследования. Теперь можно точно указать, какие классы могут наследоваться от вашего типа.
По сути, это золотая середина между публичными классами (наследовать может кто угодно) и final классами (наследовать нельзя вообще). Вы сами решаете, кто входит в "белый список" наследников.
🔹 Зачем они нужны
Обычное наследование имеет проблемы:
— невозможно гарантировать закрытость иерархии (кто-то может добавить свой подтип);
— компилятор не может проверить, что все случаи покрыты (switch требует default, даже если вы обработали все варианты);
— сложно моделировать ADT (Algebraic Data Types) из функционального программирования.
Sealed классы решают эти проблемы: компилятор знает все возможные подтипы и может проверить полноту обработки в pattern matching.
🔹 Ключевые моменты
▪️ sealed — ключевое слово для объявления запечатанного класса/интерфейса.
▪️ permits — явное перечисление разрешённых наследников.
▪️ Наследники должны быть: final, sealed, или non-sealed.
▪️ Если наследники в том же файле, permits можно опустить.
▪️ Отлично работает с pattern matching и switch expressions.
🔹 Под капотом
Компилятор создаёт специальный атрибут PermittedSubclasses в bytecode, который содержит список разрешённых наследников. При загрузке класса JVM проверяет, что все указанные подклассы действительно существуют и корректны.
Pattern matching с sealed types позволяет компилятору проверить полноту покрытия без default ветки:
🔹 Подводные камни
— Обратная совместимость
Если вы сделали класс sealed в новой версии библиотеки, старый код с кастомными наследниками перестанет компилироваться.
— Видимость подклассов
Все наследники должны быть доступны sealed классу на момент компиляции. Нельзя добавить подкласс из другого модуля или jar.
— Сериализация
При десериализации sealed иерархии нужна осторожность? можно получить подделанный подтип. Используйте validation или sealed интерфейсы с records.
— non-sealed подклассы
Если сделать наследника non-sealed, он открывает дыру в иерархии и от него можно наследоваться кому угодно. Используйте осторожно.
✔️ Когда использовать
— Моделирование состояний (State machines, FSM).
— Result/Either типы для обработки ошибок без exceptions.
— Domain-driven design с явными типами (Payment может быть Card, Cash, Crypto).
— Pattern matching в бизнес-логике с гарантией полноты.
— API, где важно контролировать расширяемость.
❌ Не подходит:
— Публичные библиотеки с plugin-архитектурой.
— Когда нужна расширяемость от пользователей.
— Legacy код с активным использованием наследования.
— Простые entity/DTO классы без полиморфизма.
🐸 Библиотека джависта
#CoreJava
В Java 17 появились Sealed Classes — механизм явного контроля иерархии наследования. Теперь можно точно указать, какие классы могут наследоваться от вашего типа.
По сути, это золотая середина между публичными классами (наследовать может кто угодно) и final классами (наследовать нельзя вообще). Вы сами решаете, кто входит в "белый список" наследников.
🔹 Зачем они нужны
Обычное наследование имеет проблемы:
— невозможно гарантировать закрытость иерархии (кто-то может добавить свой подтип);
— компилятор не может проверить, что все случаи покрыты (switch требует default, даже если вы обработали все варианты);
— сложно моделировать ADT (Algebraic Data Types) из функционального программирования.
Sealed классы решают эти проблемы: компилятор знает все возможные подтипы и может проверить полноту обработки в pattern matching.
🔹 Ключевые моменты
▪️ sealed — ключевое слово для объявления запечатанного класса/интерфейса.
▪️ permits — явное перечисление разрешённых наследников.
▪️ Наследники должны быть: final, sealed, или non-sealed.
▪️ Если наследники в том же файле, permits можно опустить.
▪️ Отлично работает с pattern matching и switch expressions.
public sealed interface Result<T>
permits Success, Failure {
}
public final record Success<T>(T value)
implements Result<T> {}
public final record Failure<T>(String error)
implements Result<T> {}
🔹 Под капотом
Компилятор создаёт специальный атрибут PermittedSubclasses в bytecode, который содержит список разрешённых наследников. При загрузке класса JVM проверяет, что все указанные подклассы действительно существуют и корректны.
Pattern matching с sealed types позволяет компилятору проверить полноту покрытия без default ветки:
return switch(result) {
case Success(var value) -> process(value);
case Failure(var error) -> handleError(error);
};🔹 Подводные камни
— Обратная совместимость
Если вы сделали класс sealed в новой версии библиотеки, старый код с кастомными наследниками перестанет компилироваться.
— Видимость подклассов
Все наследники должны быть доступны sealed классу на момент компиляции. Нельзя добавить подкласс из другого модуля или jar.
— Сериализация
При десериализации sealed иерархии нужна осторожность? можно получить подделанный подтип. Используйте validation или sealed интерфейсы с records.
— non-sealed подклассы
Если сделать наследника non-sealed, он открывает дыру в иерархии и от него можно наследоваться кому угодно. Используйте осторожно.
— Моделирование состояний (State machines, FSM).
— Result/Either типы для обработки ошибок без exceptions.
— Domain-driven design с явными типами (Payment может быть Card, Cash, Crypto).
— Pattern matching в бизнес-логике с гарантией полноты.
— API, где важно контролировать расширяемость.
— Публичные библиотеки с plugin-архитектурой.
— Когда нужна расширяемость от пользователей.
— Legacy код с активным использованием наследования.
— Простые entity/DTO классы без полиморфизма.
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍4❤3👏1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁20👍4🔥3