Java: fill the gaps
前往频道在 Telegram
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
显示更多📈 Telegram 频道 Java: fill the gaps 的分析概览
频道 Java: fill the gaps (@java_fillthegaps) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 12 393 名订阅者,在 技术与应用 类别中位列第 9 836,并在 俄罗斯 地区排名第 52 005 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 12 393 名订阅者。
根据 28 八月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -60,过去 24 小时变化为 -1,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 0%。内容发布后 24 小时内通常能获得 N/A% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 0 次浏览,首日通常累积 0 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 0。
- 主题关注点: 内容集中在 redis, hashmap, linkedhashmap, индекс, фича 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
凭借高频更新(最新数据采集于 29 八月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
12 393
订阅者
-124 小时
-157 天
-6030 天
帖子存档
12 393
10 правил безопасных java приложений.
Скорость, масштабируемость, удобство использования — такие требования обычно ставят заказчики. Устойчивость к атакам и защита данных редко проговаривается и подразумевается по умолчанию.
Для кода на java есть много рекомендаций, например, Secure Coding Guidelines for Java SE. На практике большинство ошибок можно избежать очень просто:
1️⃣ Понятный код и однозначная бизнес-логика - уязвимости легко пропустить в чём-то сложном и запутанном. Сужайте и скрывайте доступную функциональность на всех уровнях:
🔸 Модификаторы полей и классов: всё, что может быть private, делайте private. Если для задачи удобно поменять private на public, подумайте о других вариантах.
🔸 Reflection и проверка имён классов: используйте осторожно, т.к такой код не адаптируется к изменениям. Поменяется имя метода, добавится ещё один класс, а рефлекшн останется тем же.
🔸 Внешний API: чем проще, тем лучше.
2️⃣ Избегайте сериализации: чтобы хранить и передавать объекты внутри системы используйте облегчённые протоколы с поддержкой версионирования: gRPC, Avro, Thrift и т.д.
3️⃣ Шифруйте персональную информацию: пароли, номера карточек, кодовые слова. Не записывайте её в лог! В 2019 году перехват незащищённых данных всё ещё входит в топ-3 опасностей по версии OWASP.
4️⃣ Проверяйте входные данные от пользователей и сторонних систем. Даже если похожие проверки есть на фронтенде. Ограничивайте размер пользовательского ввода и результатов: не позволяйте загружать 5 ГБ картинку и запрашивать триллион записей.
5️⃣ Используйте PreparedStatement для запросов к БД. Для большинства NoSQL они тоже есть.
6️⃣ Скрывайте технические детали в сообщениях об ошибке: не показывайте стек трейс конечному пользователю и в консоли браузера.
7️⃣ Обновляйте библиотеки, не игнорируйте авто-апдейты.
8️⃣ Проверяйте open-source библиотеки, которые используются на проекте, например, с помощью OWASP Dependency-Check. Коммит вредоносного кода в open-source - реальная угроза.
9️⃣ Записывайте и анализируйте активность пользователей - как минимум неудачные попытки авторизации.
🔟 Тестируйте методы на переполнение, оно случается.
12 393
Stream API: забудьте про peek()
Один из недостатков Stream API по сравнению с циклами - неудобный дебаг. До сих пор метод
peek() был единственным способом посмотреть элементы внутри стрима:
list.stream().filter(..)
.peek(e->println("filt:"+e))
.map(..)
.peek(e->println("map:"+e))
...
Недавно в Intellij IDEA вышел потрясающий апдейт, который выводит дебаг стримов на новый уровень:12 393
Stream API: обзор и основные методы.
Stream API - удобный инструмент обработки данных, который появился в java 8. Два принципа, на которых строится Stream API - composition & single responsibility. Действия собираются из простых независимых друг от друга блоков без сайд-эффектов. Этим достигается удобство чтения и поддержки:
✅ Стандартные названия.
✅ Нет локальных переменных.
✅ Линейная последовательность действий.
Стрим состоит из 3 частей:
1️⃣ Источник данных.
2️⃣ Преобразования.
3️⃣ Конечная операция.
Основные способы создать стрим:
🔸 Из готовой коллекции
🔸 Из набора элементов
🔸 Из массива
🔸 Задать первый элемент и правило вывода последующего, получится бесконечный стрим:
Stream.iterate(T, BinaryOperator)
🔸 Задать первый элемент, правило вывода последующего и условие остановки: Stream.iterate(T, Predicate, BinaryOperator) (java 9)
🔸 Генерировать независимые друг от друга элементы: Stream.generate(Supplier)
🔸 Из диапазона с включением граничных значений: IntStream.range(..)
🔸 Диапазон без граничных значений: IntStream.rangeClosed(..)
🔸 Из строк: BufferedReader.lines()
🔸 Из символов строки: CharSequence.chars()
Названия большинства методов преобразований говорят сами за себя: filter, distinct, limit, sorted. Есть менее понятные:
🔹 map: применить функцию к каждому элементу.
🔹 flatmap: положить элементы из "списка списков" в единый стрим. Также с его помощью можно уменьшить размерность массива.
🔹 takeWhile(Predicate): брать элементы из стрима, пока выполняется условие. Метод доступен c java 9.
🔹 dropWhile(Predicate): пропускать элементы, пока условие не нарушится. Метод доступен c java 9.
🔹 peek(Consumer): сделать что-то с каждым элементом стрима, не меняя исходный стрим. Часто используется при дебаге, например, вывести в консоль текущие элементы.
Вычисления выполняются не сразу, а только когда вызывается конечная операция. Конечная операция описывает желаемый результат. Результатом может быть:
◾️ Структура данных: toArray, collect.
◾️ Результат поиска: anyMatch, allMatch, nonMatch, findFirst, findAny.
◾️ Результат агрегации: min, max, count, reduce.
◾️ Сайд-эффект: forEach, forEachOrdered.
Стрим - не структура хранения данных. Он обходит источник данных (или сам его генерирует) и сохраняет промежуточные результаты. По этой причине нельзя копировать стримы и исполнять их несколько раз - пришлось бы контролировать этапы исполнения, консистентность данных и добавить синхронизацию для работы в многопоточной среде. При попытке переиспользования стрима выбрасывается IllegalStateException.
✅ Исходные данные не меняются.
✅ Стрим нельзя переиспользовать.
✅ Пока не вызвана конечная операция, стрим можно менять как угодно.
Пример из опроса выше вернёт 1.
list.remove(0) удаляет элемент с индексом 0, поэтому на момент старта вычислений stream.count() в источнике данных остаётся всего один элемент.
Если Вас раздражает, что метод list.remove принимает не сам элемент, а индекс, то некоторые методы Stream API Вас тоже разочаруют. Например, такой код компилируется:
Stream.of(-1, 0, 1).max(Math::max).get();Но в результате получается -1, потому что входной параметр метода
max - Comparator. Math.max по сигнатуре на него похож, поэтому получается неверный результат😒12 393
Что выведет следующий код?
List list=new ArrayList();
list.add(1);
list.add(2);
Stream stream=list.stream();
list.remove(0);
println(stream.count());12 393
java.util.Set: сравнение реализаций.
Set - интерфейс для коллекций из уникальных элементов. В стандартной библиотеке java 6 реализаций:
HashSet, TreeSet, LinkedHashSet, CopyOnWriteArraySet, ConcurrentSkipListSet и EnumSet.
В этой статье рассмотрим и сравним первые три из них.
Детали реализации:
1️⃣ TreeSet базируется на TreeMap, а он - на основе красно-чёрного дерева. Элементы коллекции должны реализовать интерфейс Comparable, либо экземпляр Comparable нужно передать в конструкторе, потому что compareTo - основной метод для вставки и поиска элементов.
✅ Отсортированные значения.
✅ Быстрая ребалансировка.
✅ Можно искать по ближайшему ключу - доступны функции lower(e), floor(e), ceiling(e), higher(e).
2️⃣ HashSet внутри себя содержит HashMap. Он реализован так:
Есть массив корневых элементов - их ещё называют бакетами. Размер массива по умолчанию 16.
Если нужно добавить элемент в коллекцию, то сначала вычисляется его хэш. Далее ищется индекс корневого элемента - остаток от деления хэша на размер массива. Если по этому индексу ничего нет, корнем становится новый элемент. Если есть - добавляется в структуру под ним.
В java 7 этой структурой был список, с java 8 - бинарное дерево.
Эффективность HashSet зависит от равномерности распределения хэшей. В энтерпрайз приложениях хэш вычисляется по набору полей, и равномерность часто не достигается. Поэтому на практике HashSet не всегда является оптимальной структурой данных. Если известно примерное количество элементов, можно задать loadFactor и capacity в конструкторе.
✅ Возможность оптимизации.
✅ Быстрый поиск.
❌ Значения не отсортированы.
❌ Долгая перестройка при увеличении размера коллекции.
3️⃣ LinkedHashSet расширяет класс HashSet. Помимо основной структуры новые элемент также добавляются в двусвязный список.
✅ Значения отсортированы по порядку вставки.
✅ Возможность оптимизации.
Знание деталей реализации даёт простые правила выбора:
1️⃣ Частый поиск - выбираем TreeSet.
2️⃣ Порядок не важен - HashSet.
3️⃣ Порядок вставки важен - LinkedHashSet.
Мы проверили, насколько велика разница между реализациями:
1️⃣ Добавить в Set миллион чётных чисел.
2️⃣ Вставить миллион нечётных.
3️⃣ Найти миллион случайных.
Запустили несколько раз для HashSet, TreeSet, LinkedHashSet, а также для HashSet и LinkedHashSet с заранее заданной ёмкостью в 2 миллиона. Проверили на java 7 и 11.
В качестве типов данных использовали Integer(идеальное распределение хэшей) и класс с несколькими полями (неравномерное распределение).
Результаты на картинке внизу поста. Тест не претендует на точность, но теперь можно добавить ещё несколько рекомендаций:
✅ В новых версиях java все операции всех реализаций работают на 5-20% быстрее.
✅ Выбор реализации имеет значение.
✅ Знаете будущий размер коллекции - передавайте его в конструкторе.
✅ Для чисел лучше использовать HashSet.
✅ Для экземпляров классов всё неоднозначно, имеет смысл попробовать разные варианты.
12 393
Функциональный стиль в java: когда он нужен?
Большинство задач в энтерпрайзе делятся на две группы:
1️⃣ Одиночный запрос.
Покупка в интернет-магазине, вывод курса доллара — такие запросы затрагивают мало данных и много компонентов. Эти бизнес-процессы хорошо описываются объектами и отношениями между ними.
2️⃣ Обработка данных.
Статистика покупок, поиск рекомендаций, прогноз погоды - в центре внимания обработка данных, а не взаимодействие сущностей. Задача разработчика — чтобы обработка была быстрой. Нужно по максимуму использовать ресурсы процессора и доступную память, снизить время простоя и количество блокировок.
Вот здесь пригодятся некоторые подходы функционального программирования(ФП) :
1️⃣ Понятие чистой функции.
В многопоточном программировании главная проблема — работа с общими переменными. Чтобы безопасно их обновлять и всегда читать актуальные знвчения используются средства синхронизации. Большинство из них снижают общую пропускную способность.
Чистые функции - методы, которые характеризуются только входными и выходными данными. У них нет сайд-эффектов, Они не меняют внешних объектов и не бросают эксепшены. Для одинаковых входных данных результат всегда будет один и тот же.
✅ Нет общих переменных — нет синхронизации.
✅ Можно кэшировать некоторые результаты.
2️⃣ Неизменяемые данные
Исходные данные не должны меняться. Меняться могут только локальные переменные.
✅ Синхронизация не нужна — общие переменные не меняются и всегда актуальны.
Пример: добавление элемента в список.
Подход ООП: добавляем в исходный список - меняем внешний объект.
public void add(List list, Integer value)
{ list.add(value); }
✅ Минимальный расход памяти.
❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
public List add(List list, Integer value {
List newList=new List();
newList.addAll(list);
newList.add(value);
return list;
}
✅ Можно использовать в многопоточной среде.
✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.12 393
Привет! Чтобы писать понятные и полезные посты, мы хотим знать больше о Вас и о том, что Вам интересно. Ответьте, пожалуйста, на 2 вопроса выше☝️
Ещё 4 коротких вопроса в https://forms.gle/Vqdfpjfm59BmPQBa6
12 393
5 причин, из-за которых код превращается в легаси.
И это вовсе не устаревание технологий и долгий срок проекта. Если для изменения одного поля в API нужно 3 дня разбираться в системе и делать пул-реквест на 75 файлов — это явный признак легаси-системы.
Сложно разобраться, сложно изменить и хочется забыть.
Главная проблема таких систем — это over-engineering.
Почему так происходит?
1️⃣ Предсказание бизнес-требований.
Прочитав задачу, программист тут же видит точки расширения и проектирует под это абстракции. Тратит две недели на фундамент, чтобы потом быстро добавить новые форматы здесь, новые типы тут, новые алгоритмы там.
Но задача бизнеса — не реализовать всё, что только можно. Задача бизнеса - быстро тестировать идеи и извлекать прибыль из самых перспективных.
✅ Не додумывайте требования бизнеса, просто реализуйте их.
2️⃣ Систематизация бизнес-требований.
Если в системе появляется много функций, программисты склонны их группировать и обобщать. Они используют принцип DRY(don't repeat yourself) и создают общие компоненты, которые принимают на вход стратегии обработки и выдают обобщённые типы данных. Чтобы встроить новые бизнес-требования в такие абстракции, абстракции абстрагируются ещё больше.
✅ Не смешивайте функциональность. Если код двух методов похож, но относится к разным бизнес-процессам, оставьте всё как есть.
✅ Используйте «правило трёх»:
▪️ Делая что-то в первый раз, вы просто это делаете.
▪️ Делая что-то аналогичное во второй раз, вы морщитесь от необходимости повторения, но все-таки повторяете то же самое.
▪️ Делая что-то похожее в третий раз, вы начинаете рефакторинг.
3️⃣ Чистота кода.
Часто разработчики относятся к задачам как к интеллектуальным головоломкам,у которых есть некоторое «красивое решение»: соответствует принципам SOLID, использует паттерны GoF и распределяет функциональность по слоям – контроллер, сервис, DAO и т.д.
Цель понятна — будущая простота изменений. Но эти возможности часто остаются невостребованными. Не каждому микросервису нужна сложная программная архитектура, а даже 4 if-а подряд понять легче, чем иерархию абстрактных классов.
4️⃣ Велосипеды.
ORM, маппинг JSON, логгирование, кэширование, сбор и анализ статистики — эти задачи уже давно реализованы в библиотеках или фреймворках.
Главная мотивация написания велосипедов в том, что существующие решения кажутся избыточными. Кажется, что свой вариант будет короче и эффективнее. Это маловероятно. Скорее всего Вы потратите много времени на разработку, поддержку и тестирование, а результат будет хуже.
✅ Ищите и используйте готовые решения.
5️⃣ Дедлайны.
Если до этого проблемой было чрезмерное усложнение, то перед дедлайном в коде появляются «костыли». Это код, который решает узкую проблему в неподходящем месте системы. Самые ответственные разработчики при этом добавляют комментарий:
// todo: refactor
Такие артефакты часто остаются в коде, иногда даже дополняются или переносятся в абстракции.
✅ Если Вы знаете, что пишете костыль — не оборачивайте его в абстракции, не приукрашивайте. Пусть он будет максимально понятным и грубым. Идеально — заведите отдельную задачу и сразу же пропишите, как исправить ситуацию.
Цель этого поста — не показать, что абстракций делать нельзя, а принципы проектирования нужны только для собеседований.
В больших системах over-engineering неизбежен. Однажды она станет тем самым душным легаси, в котором никто не хочет разбираться. В наших силах только замедлить этот процесс. Помните, что главная цель кода — решать бизнес-задачи. А паттерны, принципы и лучшие практики — это только инструменты для достижения этой цели.12 393
Виш-лист разработчика: работа в стартапе и зарплата в долларах.
Пост рекламный, но о важных вещах.
В карьере разработчика сложны только первые шаги — войти в индустрию и набрать 3-4 года опыта. Спрос на программистов сильно превышает предложение, поэтому в IT большие возможности для самореализации.
Можно продвигаться в карьере, увеличивать зарплату или устраивать себе челленджи, например:
🔸️ Пройти путь от джуниора до архитектора за 5 лет.
🔸️ Разобраться в какой-нибудь теме, выступить на конференции и носить футболку с шуткой, которую поймут только 3 человека в мире.
🔸️ Пройти 8 этапов собеседований в Яндексе и отказаться от оффера.
Участвовать в молодых проектах — тоже интересная цель. Работа в стартапе совсем не похожа на уютный корпоративный мир. Это:
✅ супер увлечённые люди,
✅ ответственность и дисциплина,
✅ быстро видны результаты работы,
✅ топовые технологии.
Конечно, также велика вероятность:
▪️ ненормированного рабочего дня,
▪️ истеричных коллег,
▪️ закрытия проекта через 3 месяца.
Многие только после работы в стартапе поняли, что значить РАБОТАТЬ. Не закрывать задачи в джире и лениво отчитываться на стендапах, а решать проблемы здесь и сейчас, адаптироваться к изменениям, быстро внедрять новые технологии.
Работа с иностранными заказчиками — тоже специфический опыт. Удалённая работа, высокая зарплата, иностранный язык и иногда совсем другой менталитет.
Главная сложность — найти такие вакансии. На HeadHunter они если и появляются, то под названием «ИП Иванов А.И» или «ООО НьюСофт». Чаще всего их там нет🤷♂️
@profunctor_jobs — телеграм канал с супергорячими вакансиями и важной информацией — стек, зарплата, локация, график и контакт. Там ищут не только java разработчиков, но это реальный шанс найти интересные предложения. Среди их партнёров:
🤳Snap - компания владеет мессенджером Snapchat, умными очками Spectacles, сервисами Bitmoji и Zenly.
🚗Bolt – транспортная платформа, работающая в 30 странах.
💲Revolut – мультивалютные карты без процентов за конвертацию.
Рекомендуем подписаться и следить за вакансиями: @profunctor_jobs
12 393
Equality: best practices. Или как правильно переопределить метод equals.
Полный список в конце поста.
Базовая реализация в классе
Object проверяет только равенство ссылок - по умолчанию каждый экземпляр класса равен только самому себе. Часто рекомендуют для новых классов переопределять метод equals и проверять равенство по значимым полям.
Несмотря на кажущуюся простоту, в реализации equals есть несколько тонких моментов. Кода в этом методе мало, выглядит он просто, а тесты на равенство редко кто пишет. Тем не менее часто встречается ситуация:
Странное поведение системы ➡️ неделя дебага по всей бизнес-логике ➡️ ошибка в equals ➡️ гнев и разочарование.
Простой способ не ошибиться — не переопределять equals вообще. Иногда это уместно:
1️⃣ Для перечислений (enum)
2️⃣ Когда важны сами экземпляры, а не данные внутри них. Пример - классы Thread, RecursiveTask.
3️⃣ Сравнение объектов вообще не предполагается. Пример - класс Pattern.
4️⃣ В базовом классе уже есть equals и нам подходит эта реализация.
5️⃣ Класс имеет модификатор доступа private или по умолчанию и мы уверены, что метод equals не будет вызван.
В остальных случаях его лучше переопределить. Во многих проектах при этом используется аннотация @EqualsAndHashCode библиотеки lombok, ограничения при её использовании мы тоже рассмотрим.
Итак, при реализации equals нужно учитывать следующие свойства:
1️⃣ Рефлексивность — экземпляр должен быть равен сам себе.
2️⃣ Консистентность — если экземпляры равны, то без внешних воздействий они должны оставаться равны.
✅ Сравнивать только неизменяемые поля.
Чтобы обезопасить себя от проблем с equals по возможности используйте для ключей в Map не классы целиком, а их неизменяемые поля, например, id.
Если используете аннотацию @EqualsAndHashCode явно указывайте поля, по которым идёт сравнение. Это поможет избежать ошибок при добавлении в класс изменяемого поля.
✅ @EqualsAndHashCode(of={“x”,“y”})
3️⃣ Симметрия.
Если в системе находятся объекты базового класса и его наследника нужно чётко понимать, как они будут между собой сравниваться.
Возьмём класс Точка, где равенство проверяется по координатам.
И его наследника — класс ЦветнаяТочка, где сравниваются координаты и цвет.
Точка обычная=new Точка(1,1);
Точка красная=new ЦветнаяТочка(1,1,RED);
Тогда обычная.equals(красная) будет true, а результат красная.equals(обычная) зависит от требований к системе — нужно ли игнорировать цвет в этом случае или нет.
4️⃣ Транзитивность.
Это свойство тоже легко нарушить, если мы имеем дело с иерархией:
Точка красная=new ЦветнаяТочка(1,1,RED);
Точка обычная=new Точка(1,1);
Точка синяя=new ЦветнаяТочка(1,1,BLUE);
Если мы решили игнорировать цвет при сравнении разных типов, то допустима такая ситуация:
красная.equals(обычная) - true
обычная.equals(синяя) — true
красная.equals(синяя) - true 😥
✅ Для иерархий классов писать юнит-тесты на симметричность и транзитивность.
✅ Не использовать @EqualsAndHashCode для иерархий классов.
🤔 Если в системе используются объекты базового и произвольного типов рассмотрите вариант замены наследования на композицию.
5️⃣ Сравнение существующего объекта с null должно возвращать false.
✅ Добавить отдельную проверку if(o == null)
Итого:
1️⃣ Не переопределяйте equals, если сравнение не предполагается.
2️⃣ Используйте в equals только неизменяемые поля.
3️⃣ Для иерархии классов пишите тесты на симметричность и транзитивность.
4️⃣ Для ключей в Map желательно использовать неизменяемые поля, например, ID.
5️⃣ Не использовать @EqualsAndHashCode для иерархии классов.
6️⃣ При использовании @EqualsAndHashCode явно прописывайте поля.
7️⃣ Кратко проверить два поля на null и равенство можно через Objects.equals(this.a, а)
8️⃣ Проверяйте первыми поля, которые вероятнее всего отличаются.
9️⃣ Не забывайте про hashcode — используйте те же поля, что в методе equals.12 393
Собеседование: план рассказа о прошлом опыте.
Техническое собеседование обычно состоит из трёх частей:
1️⃣ Синтаксические и алгоритмические задачки
2️⃣ Рассказ о прошлом опыте
3️⃣ Вопросы на свободную тему
Первая часть часто проходит по шаблону вопрос-ответ, её цель - проверить бэкграунд и знание языка. Но интервьюера также интересует опыт кандидата, кругозор и навыки общения. Содержательный рассказ о прошлых проектах - это по сути самопрезентация. Она помогает работодателю понять, подходите Вы для вакансии или нет.
Чтобы не тратить время на лишнее, сфокусироваться на главном и ничего не забыть рекомендуем организовать рассказ по следующей схеме:
🔶️Проекты, над которыми Вы работали больше 2 лет назад. 2-3 предложения о теме проекта и основных технологиях.
🔶️ Предпоследний проект — год-полтора назад. Тоже кратко, 4-5 предложений.
🔶️ Последний проект. Это самая актуальная и важная информация, поэтому уделите описанию большую часть времени.
Для описания проекта хорошо подходит шаблон «сверху вниз»:
1️⃣ Цель проекта: решаемые бизнес-задачи.
2️⃣ Общая схема: верхнеуровневая архитектура и основные технологии.
3️⃣ Контекст: компонент, который разрабатывала Ваша команда. Покажите на общей схеме, кратко опишите функции и уточните технологии. Расскажите о составе команды, процессе, по которому работали и с кем ещё взаимодействовали.
4️⃣ Ваша деятельность. Эта часть - кульминация, лучше подготовить её заранее. Кратко расскажите о фичах, которыми Вы занимались. Не углубляйтесь в бизнес-описание, лучше подробнее расскажите о результатах, решённых проблемах, какие подходы и инструменты использовали. Если какие-то из них упоминаются в вакансии — сделайте на них особый упор. Чем больше самопрезентация будет опираться на требования вакансии — тем больше шансов получить оффер.
Такое описание проектов максимально выделит Ваши навыки и достижения. Интервьюер увидит, что Вы:
▪️понимаете задачи бизнеса
▪️видите общую картину и свой вклад в неё
▪️понимаете процессы внутри команды
▪️умеете решать проблемы
▪️ориентированы на результат
Помните, что направление рассказа о предыдущем опыте — не углубляться в технические детали, а показать, что Вы ответственный сотрудник, решаете поставленные задачи и хорошо работаете в команде.
Ведь именно этого ждёт будущий работодатель.
12 393
Как создать поток: 7 вариантов ответа.
Выбирайте любой или комбинируйте несколько, ведь это отличный шанс показать широкий кругозор и перевести разговор в интересную Вам сторону.
1️⃣ Синтаксический ответ — ожидаемый.
🅰️ реализовать интерфейс Runnable или Callable и передать новому объекту Thread:
Runnable action = () -> System.out.println("hey");
new Thread(action).start();
🅱️ расширить класс Thread, переопределить метод run, создать экземпляр этого класса:
class MyThread extends Thread {...}
new MyThread().start();
2️⃣ Углублённый ответ.
new Thread(...) приведёт к созданию в heap экземпляра класса Thread и сопутствующих объектов —program counter, хранилище thread-local переменных, нескольких стеков, объектов синхронизации и т.д. Вызов метода start() вызывает нативный метод start0(). Через JNI исполнится метод JVM JVM_StartThread, реализованный на C++, который вызывает метод ОС для создания потока. Для Unix это pthread_create.
3️⃣ Практический ответ.
Создание потока - ресурсоёмкая операция, поэтому лучше создать пул потоков, которые можно переиспользовать.
ExecutorService executor = Executors.newCachedThreadPool();
Runnable action = () -> System.out.println("hey");
executor.submit(action);
4️⃣ Неожиданный ответ.
Понятие потока в программировании очень размыто, особенно с учётом русского языка. Thread - структура для параллельного выполнения задач с помощью планировщика и потоков ОС.
Поток данных - тоже поток. В случае Stream API это скорее статичная коллекция, но можно рассказать интервьюеру основные методы его создания:
list.stream()
Stream.of(...)
Stream.range(...)
Arrays.stream(...)
Stream.generate(...)
5️⃣ Хайповый ответ.
Раз мы заговорили о потоке как потоке данных, то здесь можно свернуть в тему реактивного программирования. Самые популярные библиотеки для java это RxJava2, Project Reactor и Akka Streams. А также интерфейсы Flow API в java 9 и Reactive Streams.
Для примера рассмотрим создание потока в Reactor.
Поток из 0 или 1 элемента называется Mono:
Mono.just(1)
Mono.fromCallable(...)
Mono.fromFuture(...)
Mono.fromSupplier(...)
Поток из N элементов называется Flux:
Flux.just(1, 2, 3)
Flux.fromIterable(...)
Flux.fromStream(...)
Flux.range(...)
6️⃣ Big data oriented ответ.
В мире big data есть много фреймворков для обработки потоков данных. Apache Spark Streaming, Storm, Flink, Samza, Kafka Streams - если Вы работали с чем-то из этого списка, то обязательно поделитесь с интервьером этим опытом.
7️⃣ Менеджерский ответ.
Рассмотрим поток с нетехнической точки зрения. Поток - состояние, когда человек полностью включен в свою деятельность, совершенно сконцентрирован на ней и испытывает удовольствие от работы.
Задача руководителя - создать сотрудникам подходящие для этого условия. Можно использовать множество методов: регулярная обратная связь, увеличения прозрачности процессов и т.д.
Ответ на банальный вопрос "как создать поток" может различаться в зависимости от опыта и текущей должности. Вопросы на собеседованиях - лишь стартовая точка для обсуждений, не стесняйтесь повернуть разговор на обсуждение Ваших компетенций и сильных сторон.12 393
Сколько времени Вы можете отвечать на вопрос "как создать поток"?
12 393
IT стендап: неожиданный способ стать сотрудником, за которого бьются компании.
У всех есть коллега, который часто предлагает неожиданные решения. Иногда неподходящие, а иногда настолько изящные, что в голову приходит мысль: "Эх, почему не я это придумал".
Чтобы стать сотрудником, которым втайне восхищаются коллеги, нужно видеть проблемы и решения, смотреть на привычные вещи под разными углами. Это сложный, но очень ценный навык, который можно развивать.
Самый доступный способ — придумывать шутки.
Знаете, как пишется материал для КВН, фильмов, мемчиков и стендапов? Редко это лёгкий и остроумный полёт мысли. Часто этот процесс состоит из нескольких шагов:
1️⃣ Погружение в тему и сбор фактов.
2️⃣ Поиск парадоксов и неожиданных сопоставлений.
3️⃣ Поиск выразительной формы для максимального эффекта.
Эти действия развивают критическое мышление, умение смотреть на вещи с другого ракурса, генерировать, фильтровать и доносить новые идеи. То, что пригодится и на работе, и в жизни.
Первый шаг уже сделан - у вас есть огромный бэкграунд разработки. Настало время идти дальше! Следующий шаг - поиск несоответствий и противоречий.
Посмотрите на привычные вещи со стороны. Есть ли в них что-то нелогичное? Необязательно сразу придумывать шутку, главное — заметить что-нибудь интересное.
Например, вот нескольких идей, вдохновлённых java 8 и 9:
1️⃣ Stream API:
▫️ожидание: название наводит на мысль о работе с потоками данных, компактном описании бизнес-процессов.
▪️реальность1: обработка статичных коллекций без использования циклов.
▪️реальность2: для дебага и обработки исключений легче переписать обратно на циклы.
2️⃣ Parallel Streams:
▫️ожидание: существенный прирост производительности при добавлении .parallel()
▪️реальность: под большой нагрузкой работает в лучшем случае на 20% быстрее, в худшем - на 15% медленнее.
3️⃣ Выход java 9:
▫️ожидание: тренд на модульную архитектуру, новый взгляд на проектирование приложений.
▪️реальность: разработчики в восторге от List.of() и Map.of().
4️⃣ Развитие фичи pattern matching:
▫️ожидание: удобный апи для регулярных выражений.
▪️реальность: проверка и приведение типа объекта пишется в одну строку, а не в две.
5️⃣ Поддержка реактивного программирование в java 9:
▫️ожидание: сверхскорость выполнения кода, новый подход к разработке.
▪️реальность: интерфейсы для паттерна Observer.
Можете развить и довести эти идеи до конца, а можете придумать свои. Сначала это будет казаться сложным, но это навык, и его можно тренировать.
Камон, шутки про java могут быть не только про кофе и многословность😉
12 393
Сборщики мусора. Плюсы и минусы G1.
Сборщик мусора(garbage collector) - компонент JVM, который размещает объекты в памяти. Выделяет место под новые и определяет те, которые уже не нужны.
В java есть 5 типов сборщика мусора - Serial Collector, Parallel Collector, Concurrent Mark Sweep (CMS), Garbage-First (G1) и Z Garbage Collector(ZGC). В java 8 вариантом по умолчанию является Parallel GC, в java 9 это G1. ZGC - самый новый, он появился в java 11.
Эффективность GC оценивается по следующим параметрам:
▪️Latency — сколько времени занимает сборка мусора. Измеряется в миллисекундах.
▪️Throughput — как часто она происходит. Пропускная способность считается в процентах времени VM и рассчитывается как обратная величина. Если сборка мусора занимает 5% времени,то throughput равен 95%.
▪️Footprint — сколько ресурсов использует GC. Учитывается количество памяти для корректной работы.
Достичь хороших значений во всех трёх показателях невозможно. Для разных GC в приоритете будут разные параметры, но для каждого доступно множество опций для изменений под требования системы.
Основной принцип работы сборщика мусора — разделение объектов по поколениям.
▫️Недавно созданные объекты относятся к «молодым». Промежуточные объекты, итераторы, локальные переменные - самые вероятные кандидаты на удаление. На них сфокусирована основная часть внимания GC. Ещё они делятся на 2 подгруппы: только что созданные и выжившие после нескольких сборок мусора.
▫️Если объект пережил много циклов работы GC (по умолчанию 15), то он переходит в «старое» поколение и проверяется реже.
Деление на поколения не абстрактное. Если объект переходит в другое поколение, он физически меняет своё место в памяти, и все ссылки на него обновляются.
В ранних версиях сборщиков мусора память делилась на 4 региона:
🟥 Eden — здесь создавались новые объекты.
🟧 Survivor – для переживших несколько сборок мусора.
🟪 Tenured – для долгоживущих объектов.
🟦 Permanent – метаинформация JVM о классах, методах и тд.
Выглядело всё примерно так:
🟥🟥🟥🟥🟥🟥🟧🟧🟧🟪🟪🟦
В G1 область Permanent удалена, а метаинформация перенесена в отдельную часть памяти под названием Metaspace.
Второе изменение — память разделилась на 2000 регионов одного размера, по умолчанию 1МБ. Тип региона может меняться. Для больших объектов регионы объединяются.
Стало как-то так:
🟥🟪🟧🟧🟥🟪🟥🟥🟧🟪
Плюсы и минусы G1:
✅ Выборочная обработка группы регионов – получаем высокий latency.
✅ Предсказуемое время обработки.
✅ Сборка мусора в основном выполняется параллельно с работой приложения.
✅ Нет проблем с фрагментацией.
➖ Затраты на поддержку такой системы чуть снижают throughput.
Целевое значение пропускной способности для G1 - 90%. Для Parallel GC это значение равно 99%.
➖ Плохо работает с heap меньше 6Гб.
Чтобы проанализировать и оптимизировать работу G1, в первую очередь нужно включить логгирование, а уже затем экспериментировать с флажками JVM.
-
Xloggc:/path/to/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
Но вообще G1 адаптируется к нагрузке и прекрасно работает со значениями по умолчанию.