ch
Feedback
Java: fill the gaps

Java: fill the gaps

前往频道在 Telegram

Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat

显示更多

📈 Telegram 频道 Java: fill the gaps 的分析概览

频道 Java: fill the gaps (@java_fillthegaps) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 12 552 名订阅者,在 技术与应用 类别中位列第 10 101,并在 俄罗斯 地区排名第 52 755

📊 受众指标与增长动态

невідомо 创建以来,项目保持高速增长,吸引了 12 552 名订阅者。

根据 05 六月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -49,过去 24 小时变化为 -4,整体触达仍然可观。

  • 认证状态: 未认证
  • 互动率 (ER): 平均受众互动率为 34.71%。内容发布后 24 小时内通常能获得 N/A% 的反应,占订阅者总量。
  • 帖子覆盖: 每篇帖子平均可获得 0 次浏览,首日通常累积 0 次浏览。
  • 互动与反馈: 受众积极参与,单帖平均反应数为 0
  • 主题关注点: 内容集中在 redis, hashmap, linkedhashmap, индекс, фича 等核心主题上。

📝 描述与内容策略

作者将该频道定位为表达主观观点的平台:
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat

凭借高频更新(最新数据采集于 07 六月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。

12 552
订阅者
-424 小时
-257
-4930
帖子存档
Self-referential generic, часть 2 В прошлом посте мы выяснили, зачем self-referential generic нужен в Enum: для использования дочернего типа при реализации интерфейса родителя. Это довольно экзотичный кейс. Сегодня покажу более практичный пример, как дженерики облегчили работу с иерархией и неизменяемыми переменными. Дано: класс Delivery с информацией о доставке. Метод cancelled делает заказ недействительным. У класса есть наследник FastDelivery, в котором дополнительно хранится ID курьера:
class Delivery {
   final long id;
   final boolean isActive;

   public Delivery(long id, boolean isActive) {…}

   public Delivery cancelled() {
      return new Delivery(this.id, false);
   }
}

class FastDelivery extends Delivery {
   private final long courierId;

   public FastDelivery(…) {…}

   public long getCourierId() {
      return courierId;
   }
}

Проблема: метод cancelled возвращает объект типа Delivery, и мы теряем информацию о курьере:
FastDelivery fast = new FastDelivery(…);
Delivery cancelled = fast.cancelled();

❌ long id = fast.getCourierId();

В такой ситуации помогут self-referential generic и небольшой обходной манёвр: 🔸 Добавляем параметр в родителя
public class DeliveryᐸT extends DeliveryᐸTᐳᐳ

🔸 Создаём метод create, который возвращает нужный экземпляр
protected T create(long id, boolean isActive) {
  return (T) new Delivery(id, isActive);
}

🔸 Используем этот метод в cancelled
public T cancelled() {
   return create(this.id, false);
}

🔸 Определяем параметр в наследнике
public class FastDelivery extends DeliveryᐸFastDeliveryᐳ

🔸 Переопределяем метод create в наследнике
protected FastDelivery create(long id, boolean isActive) {
  return new FastDelivery(this.id, this.isActive, courierId);
}

Всё! Теперь информация не теряется:
FastDelivery fast = new FastDelivery(…);
FastDelivery cancelled = fast.cancelled();

✅ long id = cancelled.getCourierId();

Здесь используется комбо двух приёмов: 🔹 Метод create и его переопределение позволяют использовать поля, доступные в наследнике и вернуть нужный объект 🔹 Self-referential generic помогает вернуть нужный тип в методе cancelled Готовый код доступен здесь Резюме Рассмотрите использование self-referential generic, когда ▫️ У вас есть иерархия ▫️ Родительский тип упоминается в аргументах или возвращаемом значении Дополнительная типизация снизит количество кода, вытащит ошибки на этап компиляции и для некоторых случаев окажется очень изящным решением✨

Self-referential generic, часть 1 Кто участвовал в декабрьском адвенте, точно помнит, что еnum компилируется в наследник класса Enum:
public enum Animal {WOLF, TIGER}
public class Animal extends Enum {
  public static final Animal WOLF;
  public static final Animal TIGER;
}
Подробнее об этом и енамах в целом можно почитать тут — раз, два и три. В определении класса Enum используется конструкция, которая называется self-referential generic (или self-bound type, или recursive generic):
EnumᐸE extends EnumᐸEᐳᐳ

В этом посте расскажу, что это такое и зачем нужно. Чтобы понять, какая проблема решается, представим, что этой конструкции нет. И определение енама выглядит так:
public abstract class MyEnum implements ComparableᐸMyEnumᐳ

Пользователь определяет enum Animal и enum Converter. Компилятор превращает это в классы
Animal extends MyEnum
Converter extends MyEnum

Каждый класс должен реализовать интерфейс ComparableᐸMyEnumᐳ и метод compareTo. Чтобы не сравнивать животных и конвертеры, придётся использовать instanceof:
public final int compareTo(MyEnum o) {
   if (o instanceOf Animal other) {
      // сравниваем зверюшек
      // return  ...
   }
   throw IllegalArgumentException();
}

В самом instanceOf нет ничего плохого. Тем более этот код генерируется при компиляции и остаётся за кадром. Есть более важный момент. Пользователь может спокойно сравнить животное и конвертер, ошибка возникнет только в рантайме. Это выглядит странно, ведь enum Animal и enum Converter никак не связаны между собой. Здесь дженерик выходит на сцену:
public abstract class EnumᐸE extends EnumᐸEᐳᐳ implements ComparableᐸEᐳ

🔸 Добавляем параметр E, совместимый с классом Enum 🔸 Используем E в интерфейсе Comparable 🔸 Компилируем enum Animal в
public class Animal extends EnumᐸAnimalᐳ
🔸 Теперь Comparable использует тип Animal, и метод compareTo станет таким:
public int compareTo(Animal o)

✅ Убрали instanceOf, код стал меньше и быстрее ✅ При компиляции происходит проверка типов:
Animal zebra = Animal.ZEBRA;
Converter csv = Converter.CSV;
zebra.compareTo(csv); // не скомпилируется! Self-referential generic позволяет использовать дочерний тип в интерфейсах и методах родителя. Для некоторых кейсов этот приём здорово упрощает код и снижает количество ошибок. В следующем посте покажу ещё один пример использования. Ответ на вопрос перед постом: self-referential generic помогает ограничить сравнение разных enum между собой.

Конструкция > в определении Enum нужна чтобы
Anonymous voting

Зачем нужна конструкция <E extends Enum<E>> в определении Enum?

Последний день для ранних пташек Сегодня последний день, когда можно вписаться на курс как early bird🦅 Курс строится вокруг java.util.concurrent — боевой лошадки каждого нагруженного сервиса. В деталях изучим все классы, концепты и практическое применение. Разберём огромное количество кейсов, лучших практик и возможных ошибок. Ну и по мелочи — разберёмся с тестированием многопоточки, сравним разные подходы (реактивщина, Project Loom, корутины), подготовимся к собеседованиям. Всё шаг за шагом и с картинками:) 👨‍🦱 “Курс великолепный, не пожалел ни одного рубля, что потратил. Это уникальный курс в своем сегменте, особенно на русском рынке. Всем советую, на курсе вы найдете все ответы на интересующие вас вопросы. Более того из курса вы сможете узнать то, что просто нет в открытом доступе нигде, исключительно авторские наработки. Однозначно советую всем бэкэнд разработчикам, даже если вы не особо используете многопточку - это очень поможет вам в понимании многопоточного кода фреймворков и вообще сильно улучшит ваш кругозор. Советую брать с обратной связью - сильно увеличивает пользу от курса.” Старт: 5 июня Длительность: 9 недель ✅ Оплата за счёт компании ✅ Рассрочка на 3 или 6 месяцев ✅ Налоговый вычет 13% Завтра цена вырастет, присоединяйтесь сегодня! http://fillthegaps.ru/mt

Что изменилось в этом потоке? Курс — мой любимый пет-проект, который я развиваю уже третий год. Казалось бы, уже 7 потоков прошло. Всем всё нравится — половину мест с обратной связью уже разобрали, отзывы отличные. Что ещё улучшать? А вот есть что:) ✅ Практические задания Практика — самая сильная часть курса. Теория осталась плюс-минус такой же с 2021 года, а практическая часть постоянно развивается. Тесты, написание кода, анализ реального кода, лабораторные работы — только так появляется уверенность при работе с многопоточкой. Для этого потока добавила пару классных примеров из Spring core на разбор, отшлифовала формулировки тестов, написала несколько гайдов для самопроверки для тарифа без обратной связи ✅ Предобучение До прохождения курса многие вообще не трогали многопоточку. И для некоторых учеников нагрузка оказывается очень серьёзной. Чтобы чуть снизить уровень стресса, учёба теперь делится на два шага: 1. Предобучение — в спокойном темпе изучить основы и потренироваться на простых примерах 2. Основной курс — закрепить основы и углубиться в детали В итоге ▫️ Новички чуть больше работают с базой и основной курс зайдёт легче (я надеюсь) ▫️ Опытные ребята пропускают предобучение и не тратят время на лёгкие задачки Подготовительный этап совсем небольшой, поэтому решила сделать его открытым. Если хотите подтянуть основы многопоточности — welcome ✅ Налоговый вычет Если вы платите налоги в России, то в следующем году можно подать заявление в налоговую и вернуть 13% стоимости курса!!! Ученики февральского потока тоже могут оформить вычет! Как это сделать и какие документы нужны — написала на сайте в разделе "популярные вопросы". https://fillthegaps.ru/mt

Анонс курса по многопоточке Старт: 5 июня Длительность: 9 недель Кто давно ждал и уже готов → http://fillthegaps.ru/mt Теперь подробнее. У курса две основные задачи: ✅ Научиться писать хороший многопоточный код Разберём типовые энтерпрайзные задачи, огромное количество кейсов, лучших практик и возможных ошибок. Сравним производительность разных решений для разных ситуаций ✅ Подготовимся к собеседованиям, где требуется concurrency. Обсудим стандартные и нестандартные вопросы, порешаем тестовые задания Что говорят ученики: 👨‍🦱 “Курс понравился тем, что он "от разработчиков разработчикам": примеры реальных библиотек для разбора, приближенные к реальным задачи для кодинга” 👨‍🦱 ”Курс очень интенсивный, охватывает не только многопоточку, но и смежные темы, учит разным лайфхакам полезным для практического использования, обращает внимание на темы, которые легко или упустить, изучая тему самостоятельно, или вообще можно никогда не узнать без курса” 👨‍🦱 ”Есть очень много свежей информации, которую сконцентрировано в едином источнике не получить” 👨‍🦱 “Это не с нуля совсем курс, и больше про правду разработки, разбавленную вопросами с собесов, а не про чистые знания.” Отзывы целиком можно почитать тут Для какого уровня курс? Middle и выше Сколько стоит? До конца пятницы (19.05) цена такая: 🔸 Без обратной связи: 24900 30000 руб. 🔸 С обратной связью: 46900 60000 руб. (разница между тарифами) ✔️ Есть рассрочка на 3 и 6 месяцев ✔️ Принимаются карты любых банков ✔️ Курс можно оплатить за счёт компании Аналогов у курса нет. Вообще:) С каждым потоком программа становится лучше, задания интереснее, а учёба приятнее. Если хотите разобраться с многопоточкой, и вам близок мой стиль изложения — записывайтесь, будет очень полезно! http://fillthegaps.ru/mt

Структура проекта и качество кода, часть 2 В прошлом посте мы рассмотрели основные структуры, по которым делаются проекты. Структура помогает легко ориентироваться в коде, плюс снижает связность между компонентами за счёт модификаторов доступа. Просто так использовать default класс из другого пэкеджа (то есть повысить связность) не получится, код не скомпилируется. Либо придётся менять модификатор доступа, что точно будет заметно на ревью. Но кое-что разрушает эту прекрасную картину: ✨фреймворки✨ Чтобы Spring мог сотворить волшебство, приходится немного жертвовать изоляцией. Начиная с public репозиториев и заканчивая одним контекстом на всё приложение. При использовании спринга (или других фреймворков) связность между компонентами меньше ограничивается и с течением времени растёт. Но выход есть! Поделить функциональность не на пэкеджи, а на Maven/Gradle модули: 📂 registration — 📂 src — Controller, Service, Repository — 📂 test pom.xml 📂 export ⚠️ Обратите внимание, каждый модуль — просто набор классов и тестов, а не отдельный микросервис! Связность при таком подходе снижается ещё больше: ✅ У каждого модуля свой набор зависимостей ✅ Нет общего контекста Можно, наверное, поделить приложение на java модули, но модули Maven/Gradle встречаются гораздо чаще. Совсем большие проекты идут ещё дальше. В Hexagonal/Clean/Onion/… architecture каждый бизнес-сценарий делится на модули бизнес-правил, адаптеров, инфраструктуры и тд. ✅ Минимальная связность, ультра простое тестирование 😐 Количество модулей, классов и интерфейсов увеличивается в разы 😐 Легко скатиться в карго-культ, нужен опыт для правильной реализации Резюме Spring — классный фреймворк, и здорово облегчает рутинные задачи. Но у него есть тёмная сторона — благодаря общему контексту связность кода неизбежно повышается. Чтобы проект не превратился в болото, в первую очередь нужен высокий профессиональный уровень всей команды. Если приложение большое, имеет смысл поделить его на отдельные модули. У каждого бизнес-процесса будет свой контекст и набор зависимостей. Поддерживать такую структуру будет гораздо проще👍

Структура проекта и качество кода, часть 1 Структура проекта — это то, как мы раскладываем классы по папочкам. Хорошая структура помогает не только ориентироваться в проекте, но и писать более качественный код. Сейчас покажу, как это работает. Разделение по слоям Начнём с структуры, которая встречается в большинстве туториалов и пет-проектах начинающих: 📂 controller — UserController, TicketController 📂 service — UserService, TicketService 📂 repository — UserRepository, TicketRepository Чтобы классы могли использовать друг друга, все классы и методы должны быть public. В такой структуре естественным путём повышается связность. Если в UserService хочется узнать номер билета, то самое простое — добавить TicketRepository и вызвать нужный метод. Всё связано со всем. Поменяешь в одном месте — сломается в другом. В пет-проекте с этим можно справиться, но для коммерческих проектов такая структура не подходит Разделение по функциям Складываем в один пекедж всё, связанное с какой-то сущностью. Оставляем 1-2 класса с модификатором public, остальным даём дефолтный модификатор доступа: 📂 user — UserController, UserService, UserRepository 📂 ticket — TicketController, TicketService, TicketRepository 📂 export — ExportService, ExcelFormatter Дефолтный модификатор ограничивает доступ между пэкеджами. Если UserService хочет сформировать отчёт по пользователям, он вынужден идти через ExportService, потому что ExcelFormatter ему не виден. ✅ Связность классов снижается, упрощается поддержка и тестирование 😐 Каждый класс решает не бизнес-задачу, а инфраструктурную. UserRepository — точка доступа к таблице users. UserService — класс по работе с классом User. Классы становятся огромными 😐 Высокая связность между бизнес-кейсами. Появляются десятки универсальных методов, которые "переиспользуются" в бизнес-сценариях. Например, создание и редактирование пользователя часто делают через один метод. Меняем одно — неизбежно задеваем похожие сценарии. Разделение по бизнес-кейсам Складываем в один пекедж все классы, связанные с бизнес-процессом. Большинство классов стоит с default модификатором и недоступна за пределами пэкеджа: 📂 newUser — NewUserController, NewUserService, UserRepository 📂 buyTicket — BuyTicketController, BuyTicketService, TicketRepository 📂 refundTicket — … 📂 export — … Количество классов увеличивается, но они становятся меньше и более изолированными. Связность между бизнес-сценариями максимально снижается. Итого: чёткая структура проекта и модификаторы доступа снижают связность между компонентами на уровне компиляции. Однако очень мало проектов используют эту практику. Не потому что разработчики плохие, а потому что на большинстве проектов этот подход не сработает. Почему так получается и кто виноват — расскажу в следующем посте:)

Как написать компаратор Компаратор задаёт правило сравнения элементов между собой. Делает он это с помощью метода compare:
public int compare(T o1, T o2) {…}

Если метод вернул ▫️ число больше нуля — первый элемент больше второго ▫️ 0 — элементы равны ▫️ число меньше нуля — первый меньше второго Простейшая и популярная реализация — вычесть одно значение из другого:
(o1, o2) -> (int) (o1.getSum() - o2.getSum())

❓ Что с этим не так? Я всегда сомневаюсь, что из чего вычитать. Если вы отвечали на опрос дольше одной секунды, значит мы в одном лагере:) Компаратор — совсем не то место, где мозг должен спотыкаться. В Java 8 в интерфейсе Comparator появился удобный метод:
orders.sort(comparing(Order::getSum))

Что классно: ✅ Не надо вспоминать, что из чего вычитать ✅ Легко сделать сравнение в обратном порядке:
comparing(Order::getSum).reversed()
✅ Можно учесть null:
nullsFirst(comparing(Order::getSum))
nullLast(…)
✅ Удобно сортировать по нескольким полям:
comparing(Order::getSum).thenComparing(Order::getId)

Самостоятельно обрабатывать null и писать сложные сортировки очень утомительно. Помню, как с удовольствием удаляла из проекта компараторы на 20 строк после перехода на Java 8😊 Важные нюансы: 1️⃣ comparing* В интерфейсе Comparator также доступны методы comparingInt, comparingLong и comparingDouble. Используются для полей примитивного типа, чтобы избежать лишнего боксинга. Если в классе Order Long id → используем comparing(Order::getId) long idcomparingLong(Order::getId) Не указывайте тип лишний раз. Для работы с объектами подойдёт обычный comparing 2️⃣ Нетривиальная работа с null* В обычных методах легко понять, что происходит:
comparing(A).reversed().thenComparing(Б)
= отсортировать по полю А в обратном порядке, дубликаты отсортировать по Б Методы null* выбиваются из этой схемы.
nullsFirst(comparing(Order::getSum))

означает, что первыми будут null объекты, а существующие заказы отсортируются по сумме. Этот компаратор работает для такого кода: orders.add(null); // эти элементы будут впереди orders.add(new Order(…)); // эти отсортируются по полю sum Если в списке нет null объектов, но в поле sum возможен null, придётся писать так:
…comparing(Order::getSum, nullsFirst(naturalOrder()));

Сравнение по нескольким nullable полям выглядит совсем плохо. К счастью, на практике такие задачи встречаются редко. Ответ на вопрос перед постом:
(o1, o2) -> (int) (o2.getSum() - o1.getSum())

Но лучше использовать comparing(Order::getSum).reversed()

В классе Order есть поле sum (сумма заказа). Мы хотим отсортировать заказы от большей суммы к меньшей. Что написать внутри метода orders.sort(…)?
Anonymous voting

Intellij IDEA: комментарии TODO Часто встречаются ситуации, когда нужно запомнить место в коде: ⭐️ Внести изменения по задаче, но чуть позже ⭐️ Отметить непокрытый тестами код ⭐️ Обсудить метод с коллегой Для таких случаев в IDEA есть специальный тип комментариев. Он начинается со слов TODO и выглядит так:
// TODO добавить тесты

Все такие комментарии можно посмотреть в окне TODO внизу экрана. Через него же можно перейти в нужное место кода в один клик. Если списка нет, ищите его через View → Tool Windows → TODO Помимо стандартных TODO и FIXME можно добавить свои метки, например, OPTIMIZE, ASK, TEST. Сделать это можно в File → Settings → Editor → TODO Очень удобно использовать TODO для текущих задач, чтобы ничего не забыть. Чтобы отметить код, который исправит кто-то другой, не забудьте закинуть соответствующую задачу в бэклог:)

Сортировка списка и костыль в JDK Вести абстрактные разговоры о разработке легко и приятно. Можно два часа рассуждать, что такое хорошее API, но гораздо полезнее обсудить конкретные примеры. Сегодня разберём метод сортировки. Если показать вопрос перед постом питонисту, он однозначно выберет list.sort(). Хотя бы потому что в питоне есть такой метод. Класс Integer реализует интерфейс Comparable, сортировка чисел — базовая функциональность любого языка программирования. Так что метод sort() максимально логичен. Однако в интерфейсе List нет такого метода, только
void sort(Comparator<? super E> c) {…}

Для элементарной операции сортировки чисел приходится писать
list.sort(Comparator.naturalOrder())

Код с Comparator.naturalOrder() похож на какой-то костыль. Под капотом не происходит ничего особенного, реализация компаратора очень простая: (с1, с2) -> c1.compareTo(c2) ❓ Так зачем писать так сложно? Почему в интерфейсе List нет метода sort()? Сейчас расскажу:) Java создавался как язык для больших и долгоживущих приложений, и его основные ценности — стабильность и обратная совместимость. C начала 2000-х в JDK есть метод Collections.sort(List). Статический метод, который меняет внутреннее состояние аргумента. Сейчас это порицается, но в те времена было норм. В больших компаниях классы JDK часто расширяли удобными методами, в том числе сортировкой в функциональном стиле:
CustomList sorted = list.sort();

Спустя много лет стало понятно, что экземплярные методы сортировки — это классно, и надо добавить такой метод в JDK. Чтобы текущие реализации списков не сломались, это должен быть дефолтный метод в интерфейсе List. Но есть проблема. Допустим, на проекте есть такой класс:
public class CustomList implements List {
  public CustomList sort() {…}
}

Допустим, в java 8 в интерфейс List добавили метод
default void sort() {…}

Старый метод не может переопределить дефолтный. тк возвращаемые значения не совместимы. Поэтому проекты, которые определили свой функциональный sort в начале 2000-х, перестанут компилироваться. Пользователи будут недовольны. Многие проекты полагаются на свой sort, поэтому разработчики JDK не стали добавлять его в интерфейс. Метод sort(Comparator) использовался редко, поэтому теперь он с нами. У Stream API нет проблем с совместимостью, так что для стримов есть прекрасный метод sorted(). Для коллекций метод sorted() есть в Kotlin💖 (обратите внимание на суффикс -ed, всё по правилам функционального подхода) Ответ на вопрос перед постом: отсортировать список можно так: ✅ list.sort(Comparator.naturalOrder()); list = list.stream().sorted().toList(); Если вам понравился list.sort(), значит у вас хороший вкус на API. К сожалению, у java свои загоны, поэтому этого метода в JDK нет.

Как отсортировать список ArrayList list?
Anonymous voting

HashMap и принципы SOLID На большинстве собесов спрашивают одно и то же. Как работает HashMap, принципы ООП и SOLID, разница абстрактного класса и интерфейса, жизненный цикл бина. Когда я была junior/middle, 80% вопросов повторялись на каждом собеседовании. И вопросы возникали уже у меня: 🤔 Как интервьюеры поймут, что я топчик, если спрашивают такую банальщину? 🤔 Почему в вакансии целая страница требований и технологий, но мы ничего из этого не обсуждаем? 🤔 Может на проекте всё очень плохо, а код написан на java 6? И только когда я начала собеседовать людей, то поняла, зачем это всё. Всё дело в специфике найма джуниоров и мидлов. На каждом проекте свой стэк, задачи и особенности. С рынка вряд ли придёт человек, который с ходу вольётся в проект, зная все нюансы технологий. В любом случае он будет немного доучиваться. Поэтому важно, чтобы человек имел крепкий фундамент и был приятным в общении на технические темы. И стандартные вопросы подходят для этого великолепно: 1️⃣ Это база🤌 Желание обсудить саги и агрегаты это здорово, но большую часть времени разработчик проводит с кодом. Stream API, коммиты, структуры данных — в этом не должно быть пробелов. В далёкие времена на собесах обсуждали только теорию и давали тесты с безумным синтаксисом и пропущенными скобками. Это довольно бесполезно, но всё ещё встречается. Сегодня собесы больше ориентируются на практику. Бывает, что кандидат лихо объясняет synchronization order, но не видит ошибок в простом многопоточном коде. Не смущайтесь лёгких заданий, они не так просты, как кажутся:) Время собеседования очень ограничено. Выделенные 30-60 минут лучше потратить на базу. Если с ней всё хорошо — остальное приложится 2️⃣ Легко сравнить кандидатов между собой Если 10 человек расскажут устройство HashMap, получится 10 разных ответов. Первый кандидат расскажет так, что ничего не понятно. Второй закопается в объяснении работы хэша и свернёт с темы. Третий оттараторит заученный текст и не поймёт дополнительный вопрос. Из четвёртого придётся тащить каждое предложение. Поэтому часто стандартных вопросов достаточно, чтобы понять, насколько приятно общаться с человеком и как глубоко он понимает базу. Умничку видно сразу🙂 ❓ Если спрашивают банальщину — значит проект скучный? Для младших грейдов вопросы могут вообще не коррелировать с будущими задачами. И наоборот — алгоритмы и system design не означают, что будущая работа будет интересной и разнообразной ❓ Как выделиться среди других кандидатов, если задают только общие вопросы? Если вас позвали на собес, значит вы уже прошли жёсткие фильтры, и резюме оставило приятное впечатление. Ваша задача — его закрепить Если интервьюер чем-то заинтересуется в вашем опыте, он обязательно спросит:) ❗️ Я отвечал на всё правильно, но оффер не прислали! Причина может быть вообще не в вас. Может пришёл кандидат с более релевантным опытом, или бюджет перераспределили на другие цели. Найм — очень субъективный процесс. Интервьюер всегда найдёт, к чему прицепится, если у вас фамилия, как у его бывшей жены. И наоборот, если с первых минут установился контакт, даже небольшие ошибки не испортят впечатления.

VM Options — это параметры, которые указываются при запуске JVM. В этом посте расскажу, чем они отличаются, и как безопасно перейти на новую версию java. В конце будет список самых популярных (и полезных) опций. Все JVM опции делятся на три группы: ⚙️ Стандартные Пишутся через минус и поддерживаются всеми JVM. Пример: -classpath, -server, -version ⚙️ Нестандартные Начинаются на -Х и определяют базовые свойства JVM. Могут не работать во всех JVM, но если поддерживаются, то вряд ли удалятся. Пример: -Xmx, -Xms ⚙️ Продвинутые Начинаются на -ХХ и касаются внутренних механизмов JVM. Не поддерживаются всеми JVM, часто меняются и удаляются. Пример: -XX:MaxGCPauseMillis=500 Некоторые продвинутые опции требуют дополнительных флажков. Для экспериментальных фич обязателен -XX:+UnlockExperimentalVMOptions. Многие фичи диагностики не заработают без -XX:+UnlockDiagnosticVMOptions Количество опций часто меняется. В 11 версии OpenJDK 1504 опции, а в 17 на 200 опций меньше. Цикл отключения опций не совсем стандартный. В обычном коде что-то помечается Deprecated, и спустя время удаляется. VM Options используют более длинный цикл: 🔸 Deprecate: функционал работает, при запуске появляется warning 🔸 Obsolete: функция не выполняется, JVM пишет предупреждения 🔸 Expired: JVM не запускается Многие опции очень нестабильны и часто меняются. Чтобы безопасно обновить версию java, нужно проверить набор опций через JaCoLine . Он подсветит устаревшие или уже бесполезные опции. Полезные опции для java 11 (да, недавно вышла java 20, но самая популярная версия всё ещё 11) 1️⃣ Память ▫️ Начальный размер хипа: -Xms256m в абсолютных значениях, -XX:InitialRAMPercentage=60 - в процентах от RAM ▫️ Максимальный размер хипа: -Xmx8g или -XX:MaxRAMPercentage=60 ▫️ Снять heap dump при переполнении памяти: -XX:+HeapDumpOnOutOfMemoryError. Адрес выходного файла задаётся в -XX:HeapDumpPath 2️⃣ Сборщик мусора ▫️ Serial GC: -XX:+UseSerialGC ▫️ Parallel GC: -XX:+UseParalllGC ▫️ CMS: -XX:+UseConcMarkSweepGC ▫️ G1: -XX:+UseG1GC (вариант по умолчанию) ▫️ ZGC: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC ▫️ Shenandoah: -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC Вывести статистику сборщика при завершении работы: -XX:+UnlockDiagnosticVMOptions ‑XX:NativeMemoryTracking=summary ‑XX:+PrintNMTStatistics Базовое логгирование коллектора: -Xlog:gc Максимально информативное: -Xlog:gc* 3️⃣ Посмотреть все доступные опции ⚙️ Нестандартные: java -X ⚙️ Продвинутые: java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal

Чем отличаются опции JVM, которые начинаются на -X, от опций, которые начинаются на -XX?
Anonymous voting

Best practice: имена методов в ООП и функциональном подходе В идеальном мире не нужно читать документацию и изучать внутрянку класса, чтобы правильно с ним работать. В этом посте расскажу best practice, о котором мало говорят, но который здорово повышает дружелюбность API. Начнём с вопроса перед постом. Если не искать подвох, то в sum мы ждём 0+10+20=30. Но подвох, разумеется, есть. BigInteger — неизменяемый класс. sum.add(10) создаёт новый объект, а исходная переменная не меняется. В итоге ни один add не влияет на sum. В консоль напечатается 0. Давным-давно основной парадигмой разработки было ООП. Вся работа строилась на изменении объектов, и этот подход отражался в названиях методов: sum.add(4), user.setName("Alisa"). Исключения встречались редко, их нужно было просто запомнить. С юных лет все знают, что String — неизменяемый, и просто вызвать метод недостаточно:
String str = "   Java   ";
❌ str.trim(); 
✅ str = str.trim();

Последние годы растёт тренд на неизменяемость. При вызове метода должно быть понятно: ▫️ меняется текущий объект, и надо просто вызвать метод или ▫️ создаётся новый объект, который надо куда-то присвоить Для такого случая есть best practice: ✅ Когда метод меняет внутреннее состояние объекта, имя метода начинается с глагола ✅ Методы НЕизменяемых объектов используют другие конструкции Простой пример. Чтобы изменить внутренние поля, используем метод set*:
order.setDeliveryDate(…);

Создать новый объект на основе текущего — метод with*:
order = order.withDeliveryDate(…);

Для более сложных операций нужно включить креативность. Здесь помогут: 🔸 Причастия: order.cancel(); // изменить текущий объект Order o = order.cancelled(); // создать новый 🔸 Предлоги и союзы:
String s = str.toLowerCase();
LocalDate l = now().plusDays(12); 
// вместо addDays 🔸 Существительное в чистом виде:
String sub = str.substring(1);

Цель здесь одна — показать, что текущий объект не меняется ❗️ Исключение: если класс использует Fluent API, обычно используются глаголы:
Optional opt = …
opt.map(…).filter(…) 

Итого: с изменяемыми и неизменяемыми объектами работа идёт по-разному. Имена методов подсказывают, как правильно пользоваться классом. Хорошей практикой считается использовать глаголы для изменяемых объектов, и что-то другое для неизменяемых☀️

Что выведет следующий код?
Anonymous voting

Что выведет следующий код?