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 549 名订阅者,在 技术与应用 类别中位列第 10 121,并在 俄罗斯 地区排名第 52 862

📊 受众指标与增长动态

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

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

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

📝 描述与内容策略

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

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

12 549
订阅者
无数据24 小时
-247
-4630
帖子存档
Два доклада с Joker 2021 1️⃣ Алексей Нестеров, Олег Докука — Что нового в Spring Framework 6 (1:10:58) Оба спикера никак не связаны со спрингом, просто обсуждают слухи о нововведениях. Spring 6 использует java 17 и не будет работать на версиях ниже. Дата выхода - октябрь 2022. Кодовая база Spring 6 использует модули (фича java 9). Можно скомпилировать только нужные модули, и итоговый проект займёт меньше места. Spring Native переедет в Spring Boot 3. Spring будет связывать некоторые бины на этапе сборки, а не в рантайме. Это ускорит запуск приложений и потребление памяти. Поменяются имена некоторых модулей. Например, пакет javax.* станет jakarta.* В Spring WebFlux добавится поддержка HTTPClient из JDK 11. Что удалится: Hessian, Http Invoker, JMS Invoker, JAX-WS, SecurityManager. Что удалится, но менее вероятно: поддержка Joda Time, RxJava 1/2, FactoryBean, Autowired через сеттеры, конфигурация через XML. Зачем смотреть: незачем. Информация недостоверная, много догадок, мало деталей. 2️⃣ Иван Крылов — От 11 к 17 версии Java (55:07) Это доклад НЕ про новые фичи языка. Про sealed классы, records и switch спикер отсылает на доклад Тагира Валеева Java 17 для тех, кто в танке. Два подхода к обновлению версии java в проекте: 1) Каждые полгода переходить на новую версию Вариант подойдёт для проектов, где релизы происходят часто — каждый месяц или два. Или жизненный цикл проекта — это основной релиз и поддержка нескольких следующих релизов. Плюс — всегда новая и поддерживаемая версия java Минус — раз в полгода тратить несколько спринтов на переход 2) Переходить только на LTS версию Встречается в проектах с редкими релизами — реже, чем раз в полгода. Минус — переход всегда долгий и трудный, оттягивается до последнего. Переезд на java 17 — это необходимость. Дело не в новых фичах, а в том, что поддержка старых версий ограничена и периодически прекращается. Миграция с 8 до 17 сложная, с 11 до 17 попроще. С чем могут быть проблемы при переходе ▫️ Удалены финалайзеры из FileOutputStream, FileInputStream, zip.Deflater, zip.Inflater, zip.ZipFile, color.ICC_Profile. Это связано с предстоящим удалением финалайзеров в java 18. ▫️ Удалён конструктор URLDecoder. ▫️ Многие классы в AWT и Swing поменяли видимость. ▫️ Удалён Applet API. Изменения виртуальной машины ▪️ GraalVM больше не связан с OpenJDK. Причина — административные проблемы и сложности с версионированием относительно OpenJDK. ▪️ Удалена оптимизация biased locking из-за плохой совместимости с Project Loom. ▪️ Добавлены CDS архивы для уменьшения времени старта VM. ▪️ Гибкий metaspace. Изменения в сборщиках мусора ▫️ Удалён CMS. ▫️ ZGC готов к использованию в продакшене с 15 версии, в ранних версиях очень много багов. ▫️ G1 и Shenandoah возвращают неиспользуемую память ОС. Это уменьшает потребляемую память сервиса, и сервисы в облачной среде обойдутся дешевле. Изменения в JDK ▪️ Классы по работе с TCP и UDP работают быстрее — DatagramSocketAPI, SocketAPI. ▪️ NullPointerException показывают больше информации. ▪️ Новый генератор случайных чисел. Изменения Security ▫️ Добавлены Root сертификаты с разным сроком действия ▫️ Удалён SecurityManager ▫️ Удалены модули java.security, java.rmi.activation Платформы ▪️ Добавилась поддержка Linux Alpine. ОС полезна для маленьких контейнеров в облаках. В ОС меньше команд, поэтому она более безопасна. Если маленькие контейнеры не нужны, то лучше выбрать другую ОС. ▪️ Полная поддержка ARM: унифицированный Linux порт, Windows, macOS Какие проекты не успели войти в java 17 ▫️ Loom: легковесные потоки в JVM ▫️ Panama: работа с нативным кодом, альтернатива JNI ▫️ Valhalla: новые типы данных value types — функциональные как классы и компактные как примитивы Зачем смотреть: незачем. Большую часть видео перечисляются неязыковые фичи между 11 и 17 версией. О них говорят редко, так что для кого-то информация окажется новой. Но с тем же успехом можно посмотреть полный список фич и поискать слова, которые актуальны для вашего проекта или интересны лично вам.

Потихоньку появляются доклады с Joker 2021. Конференции - это классно, но часто бывает, что: ▪️ Название доклада не отражает содержание ▪️ Тема интересная, но слушать тяжело ▪️ Доклад слишком простой или слишком сложный Поделюсь с вами summary двух самых популярных докладов с последнего джокера. Выделила основные идеи, полезные детали и сгруппировала всё, что получилось. Если тема заинтересует — посмотрите целиком. Если нет — сэкономите 2 часа жизни🙂 P.S. Все вопросы по содержанию не ко мне, а к спикерам

Enum и метод values Если вы новичок и мало знаете про enum, то лучше начать с этого лонгрида. Сегодня обсудим итерацию через метод values и как его оптимизировать. Итак, enums — это синтаксический сахар, который при компиляции превращается в обычный класс. Класс из примера выше превратится в
public final class Number extends Enum<Number
> Элементы енума станут статическими полями:
public static final Number ONE;
public static final Number TWO;
public static final Number THREE;

Внутри нового класса появится массив:
Number[] VALUES = { ONE, TWO, THREE};

И его копия будет возвращаться в методе values:
return VALUES.clone();

При каждом вызове values возвращается новая копия массива. Дело в том, что массивы — это изменяемый объект. Если возвращать ссылку на VALUES напрямую, любой желающий сможет поменять исходный массив:
Number.values()[2] = ONE;

Это небезопасно, поэтому каждый раз возвращается копия. Если цикл с values используется в высоконагруженном коде, то разумно сохранить массив в отдельную переменную и переиспользовать её:
static Number[] numbers = Number.values();

for (Number n : numbers) {…}

Если код вызывается редко, то смысла в отдельной переменной нет. Пример из жизни В Spring Web 5.2 в классе HttpStatus есть такой код:
for (HttpStatus status : values()) {
  if (status.value == statusCode) {
    return status;
  }
}

Этот цикл вызывается почти в каждом запросе, но только в этом году завели баг. К описанию прилагался бенчмарк: при нагрузке 600 запросов/сек код производил мегабайт мусора каждую секунду. Теперь код выглядит так:
private static final HttpStatus[] VALUES;

static {
  VALUES = values();
}

for (HttpStatus status : VALUES) {
  if (status.value == statusCode) {
    return status;
  }
}

Ответ на вопрос перед постом Будет создано 3 массива: один внутри класса Number и два клона при вызове values()

Сколько объектов Number[] будет создано в этом коде?
Anonymous voting

Сколько объектов Number[] будет создано в этом коде?

Что такое Serverless, часть 2 В прошлом посте рассмотрели, как инфраструктура понемногу переходила на аутсорс. Serverless — следующий этап такого перехода. AWS Lambda — самая первая и популярная платформа для Serverless, поэтому дальше буду говорить про неё. Важно! Есть ещё термин Lambda Architecture — это вообще про другое. Итак, в чём суть. Любое приложение — это набор функций. Допустим, в интернет-магазине три функции: ▫️ Добавить товары ▫️ Сделать заказ ▫️ Вывести список текущих заказов Можно запихать всё в один артефакт — получится монолит. Можно каждую функцию обернуть в отдельный сервис — получатся микросервисы. Serverless абстрагируется от этой проблемы. Мы не распределяем функциональность по артефактам и работаем просто с функциями. Проектируем не сборник фич, а отдельные маленькие компоненты. Поэтому Serverless считается архитектурой. Или отдельной моделью проектирования, если термин "архитектура" кажется вам неподходящим. Платформы, на которых запускаются Serverless приложения, называют FaaS — Functions as a Service. В Петербурге Serverless используют около 10 компаний, что не очень много. Но тренд растёт, даже Сбер уже сделал свою FaaS платформу. Как это работает? Каждая функция состоит из: ▫️ Исполняемого кода ▫️ Списка зависимостей ▫️ Списка событий-триггеров ▫️ Конфигурации — количество памяти, необходимые права, время жизни функции и тд Когда происходит событие из списка тригеров, FaaS платформа создаёт инстанс функции и обрабатывает его. Событием может быть HTTP запрос, сообщение из очереди, действие по расписанию. После обработки функция завершается или ждёт следующее событие в течение заданного времени. Станет ли код проще? Точно нет. Проектировать систему с изолированными функциями гораздо сложнее, чем слепить всё в монолит или десяток сервисов🙂 У Serverless свой деплой, тестирование и кодовая база. Даже если перенести только некоторые части приложения, общая схема заметно усложнится. Зачем переходить на Serverless? Если микросервисы в PaaS или докере нормально справляются, то должна быть веская причина что-то менять. Таких причин может быть две: 1️⃣ Функции развёртываются по необходимости. Облака это удобно, но иногда очень дорого. FaaS модель неплохо снижает стоимость при переменной нагрузке. 2️⃣ Масштабирование по умолчанию. В AWS Lambda автоматически масштабируются и сервисы, и остальные компоненты, например, БД. В докере и PaaS для этого нужно прилагать немало усилий. Сколько стоит? Допустим бэкенд мобильного приложения принимает за месяц 3 миллиона запросов, среднее время — 120мс. Выделим для одной функции 1536 МБ и процессор х86. Плата за месяц в таком случае будет 2,7 доллара. Это только AWS Lambda, остальные компоненты оплачиваются отдельно. Что ещё ✅ Никаких Ansible, Docker, bash скриптов ✅ Базовый мониторинг и аналитика из коробки ✅ Spring Cloud и плагин IDEA AWS Toolkit сильно облегчают разработку 😐 Нужны отличные навыки проектирования 😐 Своеобразное тестирование ❌ Сложно адаптировать уже существующие приложения ❌ Больше задержек, response time может увеличиться В целом Serverless — очевидный тренд, который набирает обороты. Жду в ближайшие два года тонну докладов на конференциях на эту тему, как когда-то было про микросервисы и реактивное программирование🙂

Что такое Serverless. Часть 1: предыстория По статистике 2021 года около 10% проектов уже используют архитектуру Serverless. Новые технологии — это классно, но важно понять, в чём вообще проблема и как она УЖЕ решается текущими средствами. И зачем решать её по-новому. Нужен чёткий ответ: — Зачем переходить с микросервисов на Serverless? Что мы от этого получим? Для ответа немного углубимся в историю инфраструктуры. 1️⃣ Всё своё До 2006 года в каждой IT компании была особая комната — серверная. Системные администраторы настраивали сервера, следили за обновлениями, безопасностью, блоками питания и решали тысячу других вопросов. ❌ Много расходов на оборудование и персонал ❌ Железки надо закупать, подключать и нельзя сдать обратно. Сложно наращивать мощности или оптимизировать нагрузку 2️⃣ IasS — Infrastructure as a Service В 2006 появился Amazon EC2 и начался тренд на IaaS: код запускается не на собственных серверах, а на арендованных. Это самый простой вариант облачной инфраструктуры. Сейчас подобную услугу предлагают AWS, Google, DigitalOcean, Microsoft, IBM, SAP, для патриотов есть Яндекс и Сбер. ✅ Легко добавлять и сокращать ресурсы ✅ Меньше расходы на персонал ✅ Не надо беспокоиться об отключении электроэнергии и потопах Инфраструктура всё ещё требует внимания — на арендованной виртуалке надо установить ОС, JVM, все службы и обновления, настроить компоненты и развернуть сервисы. Большая часть этих операций почти у всех одинакова. Так что дальше расходимся на две ветки: 🅰️ PaaS — Platform as a Service PaaS = IaaS + ОС + базовый мониторинг + легко подключаемые компоненты Облачные провайдеры берут на себя больше рутинных операций. Выглядит как будто работаешь с удалённой машиной. Можно довольно легко добавить БД, мониторинг, кэши, очереди и связать их между собой в настройках. Примеры PaaS: Heroku, AWS Elastic Beanstalk и тд ✅ Много готовых компонентов — можно быстро настроить работающую систему ❌ Набор компонентов большой, но всё же ограниченный. Если использовать что-то непопулярное, то придётся искать обходные пути ❌ Сильная привязка к вендору. Выбрал PaaS от амазона — скорее всего файловое хранилище, очереди и БД тоже будут амазон. 🅱️ Докер Популярная альтернатива PaaS — докер-контейнеры в предоставленных виртуалках. В каждом контейнере свой runtime и все нужные для сервиса файлы. Для управления контейнерами есть куча инструментов — Kubernetes, Mesos, Google Container Engine. Amazon ECR и Google CR помогают с хранением докер-образов, AWS Fargate — с масштабированием. ✅ Супер гибкость. Можно собрать любые компоненты с любыми настройками и связать их как угодно ❌ Cложно выбирать и долго настраивать Заметили, что не было ни единого слова про архитектуру? PaaS и Docker только упрощают сборку инфраструктуры. Пока что нет разницы, что запускать внутри — гигантский монолит или сервис из трёх файлов. В следующем посте перейдём уже к Serverless. Поговорим, почему это называется архитектурой и что ещё готовы взять на себя облачные провайдеры.

DRY для джуниора и сеньора Раз уж пошли по базовым принципам, то сегодня разберём DRY: Don't Repeat Yourself. Такой популярный и такой обманчиво простой. Отношение к DRY эволюционирует по мере роста разработчика и проходит через четыре этапа: 🔸 Этап 1. Стажёр Боготворит DRY, считает дублирование кода ужасным грехом. Действительно, зачем писать несколько раз одно и то же? Для этого код максимально оптимизируется. Универсальные методы, универсальные статические методы, много входных параметров. 🔸 Этап 2. Джуниор Любит DRY, но понимает, что любить — значит страдать. Проект становится больше, а бизнес-логика — сложнее. Добавить в метод ещё один if уже не так просто. Сложно разбираться в коде, сложно писать тесты, но чего не сделаешь ради хорошего кода. А хороший код на этом этапе — это максимально сжатый код🙂 🔸 Этап 3. Мидл Всё ещё любит DRY, но более возвышенно — на уровне иерархий и паттернов. Когда приходится дублировать код, то грустит, что на проекте плохая архитектура. 🔸 Этап 4. Сеньор Распилил монолит на сервисы, реализовал десятки крупных фич и отдал сердце SOLID. А теперь по делу Часто начинающие разработчики считают, что хороший код — это суперконцентрированный код с кучей паттернов и хитрых приёмов. В больших проектах хороший код — это тот, который легко читать, тестировать и поддерживать. Оптимизации и приёмчики - это совсем небольшая часть кодовой базы. Если бизнес-процессы не пересекаются, то связывать их искусственно с помощью кода — плохая идея. 🙁 Класс User c 20 полями. 10 полей используются в 1 сервисе, другие 10 - в другом, половина объекта всегда пустая 🙁 Универсальный метод с 7 параметрами под разные случаи 🙁 Сложная иерархия с кучей шаблонных методов Единственный плюс — меньше кода. Зато ❌ Плохая читаемость ❌ Сложно писать тесты ❌ Сильная связность. Функцию тяжело поменять или вынести в другой модуль Я не говорю, что копипаст - единственный шанс на хорошую архитектуру. Переиспользовать код можно, если это действительно универсальные компоненты и та же цепочка бизнес-процессов. Но такое понимание приходит только с опытом.

Чем отличаются Dependency injection, Dependency invertion и Inversion of Control Прошлый пост про Liskov, как говорится, "взорвал мой директ", поэтому на этой неделе расскажу про ещё два популярных принципа. Сегодня про букву D из SOLID — Dependency Inversion. Что это, и чем отличается от Dependency injection и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре. Будем разбираться на простом примере: класс Service записывает логи в файл через класс FileLogger:
class FileLogger {…}
class Service {
  FileLogger logger=new FileLogger();
}

Сделаем код чуть лучше с помощью разных принципов: 1️⃣ Dependency injection — компоненты создаются не внутри класса, а где-то в другом месте. Как реализовать: перенести инициализацию логгера в конструктор или сеттер:
class Service {
  FileLogger logger;
  Service (FileLogger logger) {
    this.logger=logger;
  }
}

✅ Класс занимается только своей бизнес-логикой ✅ Можно вынести всю конфигурацию в одно место. Или спихнуть часть забот фреймворку, например, Spring ⚔️Историческая справка Когда Spring ещё не был популярен, в проектах использовался паттерн Service Locator. Суть: компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы:
class ServiceLocator {
  static Logger logger = …
  static Logger getLogger() {
     return logger;
  }
}

class Service {
  Logger logger=ServiceLocator.getLogger();
}

2️⃣ Dependency invertion (D из SOLID) ▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций ▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции Как реализовать: использовать интерфейс логгера, а не конкретный класс
interface Logger {…}
class FileLogger implements Logger {…}

class Service {
   Logger logger=new FileLogger();
}

✅ Интерфейс проще использовать, так как методов меньше ✅ Реализацию легко заменить ✅ Оба класса проще тестировать Термин "абстракция" используется, потому что SOLID не привязан только к джаве. Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс — наилучший вариант 3️⃣ IoC - Inversion of Control В маленьких программах жизнь начинается в методе main. Программист создаёт объекты и вызывает их методы, все шаги явно прописаны. Inversion of Control — это когда ход выполнения программы задаёт фреймворк. Например, Spring создаёт объекты, принимает запросы и не даёт программе завершиться. Как реализовать: использовать аннотации фреймворка
@Component class FileLogger {…}
@Component class Service {
  @Autowired
  FileLogger logger;
}

✅ Меньше скучного кода ✅ Низкая связность — код легко читать, менять и тестировать Резюме: 🔸Dependency injection — класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер 🔸Dependency invertion — класс работает с другими компонентами через интерфейс 🔸Inversion of Control — ход программы задаёт фреймворк ❗️Ответ на вопрос перед постом: Это словоблудие относится к Dependency injection

Паттерн проектирования, который в соответствии с принципом единственной обязанности передает другому объекту ответственность построения требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму - это:
Anonymous voting

L is for Liskov — SOLID принципы знаете? Думаю, нет разработчика, который не слышал на собеседовании такой вопрос. Расшифровку знают многие. А вот практические знания часто ограничиваются Single Responsibility и Interface Segregation. Как применять остальные буквы в ежедневной работе — не всегда понятно. Больше всего вопросов возникает насчёт L — Liskov Substitution Principle. О нём и будет сегодняшний пост. Полное определение звучит так: Пусть q(x) является свойством верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T. По-простому: если заменить класс А на подкласс B, то система будет работать корректно и без неожиданных сайд-эффектов.
A service1 = new A();
A service2 = new B();

Для наблюдателя service1 и service2 ведут себя совершенно одинаково. Класс-наследник дополняет поведение родителя, а не замещает его. В результате система работает более предсказуемо. Как это выглядит на практике: 1️⃣ Выходной тип метода в наследнике такой же как у родителя или расширенный Базовый класс: Info getInfo() Наследник:
✅ BigInfo getInfo()
❌ Object getInfo()

2️⃣ Подклассы не бросают дополнительных исключений, но могут уменьшить их список Базовый класс: void save() throws FileNotFoundException Наследник:
✅ void save()
❌ void save() throws FileNotFoundEx, InterruptedEx

Java — типизированный язык, поэтому пункты 1 и 2 контролируются компилятором. 3️⃣ Типы входных параметров те же или менее строгие. Пункт для общего понимания, тк для Java это неприменимо Базовый класс: void add(Account acc) Наследник:
✅ void add(Object acc)
❌ void add(AdminAccount acc)

Следующие пункты компилятор уже не проверит, это целиком ответственность программиста. 4️⃣ Метод подкласса делает то же, что и метод базового класса Базовый класс: метод countVisitors считает пользователей Наследник: ✅ Считает пользователей чуть по-другому ❌ Считает пользователей, обновляет статистику, сохраняет результат в БД 5️⃣ Метод наследника взаимодействует с теми же сущностями: ▪️ Метод родителя увеличивает счётчик - подкласс тоже увеличивает ▪️ Метод родителя не меняет поле - подкласс тоже не меняет ▪️ Метод родителя вызывает другие методы в определённом порядке - подкласс делает то же самое А что можно вообще? Если в подклассе объявлены новые поля, то методы подкласса могут делать с ними что угодно. На этом всё🙂 Правила выше - очень строгие. Но и наследование — штука непростая, это самая сильная связь между сущностями. Часто единственный плюс — это краткость кода, но по ходу развития проекта ограничения доставляют всё больше проблем. Нарушения принципа подстановки — повод пересмотреть иерархию наследования или совсем от неё отказаться.

8 ошибок начинающих разработчиков Рассмотрим типичную историю начинающего разработчика или стажёра. Чтобы стать программистом, стажёр долго учился. Прошёл курсы и сделал несколько учебных проектов. У них маленькая кодовая база, один пользователь, а главная задача - отработать алгоритм или попробовать фреймворк. В этих условиях у стажёра сформировался стиль написания кода. После собеседования стажёр начинает работать в крупном проекте вместе с другими разработчиками. Ему нужно перестроиться: многие паттерны, нормальные в учебных проектах, уже не подходят. В этом посте я опишу 8 таких паттернов на простых примерах. 1️⃣ Изменение входных параметров Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:
List list = new ArrayList();
process(user, list);

Во входной параметр list записывается результат метода. Такой процедурный стиль обычно идёт из учебных заданий по алгоритмам, где экономится каждый бит и максимально сокращается количество действий. В системах со сложной бизнес-логикой такой подход усложняет чтение кода, тестирование и возможную параллелизацию. Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:
List orders = getOrders(user);

2️⃣ Сложные универсальные методы Задача: по-разному считать скидку для новых пользователей и пользователей с картами лояльности. Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:
getDiscount(user, true, true, limit, true)

Как лучше: сфокусированные методы для разных ситуаций
getDiscountNew(user)
getDiscountLoyal(user)

К ситуациям выше в комплекте идут 3️⃣ Длинные методы 4️⃣ Любовь к статическим методам Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше. 5️⃣ Сложное проектирование Задача: завести три типа пользователей - новые, обычные и привилегированные. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером. Как лучше: как можно проще. Например, один класс пользователя с полем Тип. PS Все предложенные "как лучше" не всегда лучше. Но думаю, вы поняли идею. 6️⃣ Нулевое или минимальное покрытие тестами как следствие больших сложных методов и недостаточной инкапсуляции. 7️⃣ Низкий уровень ответственности Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах: 🔸 Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других: — Что с задачей, которую я тебе дала 3 дня назад? — Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё. 🔸 Код не решает проблему, а заметает симптомы: — Тесты мешали сделать пул-реквест, так что я их отключил. 8️⃣ Слабые коммуникативные навыки — Как ты починил баг с расчётом ставки? — Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил — В чём была ошибка? — Там два двоеточия вылезло — Где? — В дебаге Эти ошибки ожидаемы в начале работы, и ничего страшного в этом нет. Я их тоже делала🙂 Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.

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 Опции очень нестабильны и часто меняются. Чтобы безопасно обновить версию java, проверьте ваш набор опций через JaCoLine. Он подстветит устаревшие или бесполезные опции. Полезные опции для java 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

Intellij IDEA: редактирование кода Несколько простых приёмов для быстрой работы. Легендарные комбинации Ctrl + C Ctrl + V Ctr
Intellij IDEA: редактирование кода Несколько простых приёмов для быстрой работы. Легендарные комбинации
Ctrl + C
Ctrl + V
Ctrl + X
работают со всей строкой, на которой стоит курсор, не нужно ничего выделять. Удалить всю строку:
Ctrl + Y

Дублировать строку:
Ctrl + D

Выделить часть кода:
Ctrl + W
При каждом нажатии W захватывается всё большая область. Переместить выделенный код: Ctrl + Shift + ⬆️ Ctrl + Shift + ⬇️

Статистика по микросервисам К концу года появляется много отчётов и статистики о состоянии JVM экосистемы. В целом ситуация не сильно отличается от прошлого года: 🔸 Самая популярная версия java до сих пор 8, используется на 69% проектов. Java 11 встречается на 36% проектов, а 12 и выше - на 16%. 🔸 Самый популярный фреймворк - Spring Boot: 65% проектов. На втором месте Spring MVC: 40% проектов. 🔸 Самая популярная IDE - Intellij IDEA, система сборки - Maven. 🔸 Трендовые GraalVM, Quarkus и Micronaut нашли применение где-то на 4% проектов, цифра примерно как в прошлом году. Поэтому в этом посте поделюсь статистикой не по джаве, а по всему, что связано с микросервисами. Данные взяла из JRebel Java Developer Productivity Report 2021 и JetBrains The State of Developer Ecosystem 2021. Демография: Россия - 3% участников, в основном ответы из США, Китая и Европы. Итак, 49% проектов используют микросервисы 42% сидят с монолитом 10% применяют serverless архитектуру На чём пишут микросервисы: 41% Java 37% JavaScript 25% Python Сколько микросервисов на проекте: 34% Меньше пяти 36% 5-10 14% 10-20 16% 20 и больше Как микросервисы общаются между собой? 83% HTTP-вызовы 47% Месседж брокер 25% WebSocket 20% RPC С чем возникает много проблем: 30% Общение между сервисами 26% Масштабирование и производительность 24% Развернуть что-нибудь локально 🥲 14% Проблемы в коде микросервисов Популярные performance проблемы: 54% Долгий Response Time 39% High CPU Usage 35% Утечки памяти 26% Много открытых соединений Сколько времени занимает поднятие окружения на удалённой машине: 42% Меньше трёх минут 38% 3-10 минут 21% Дольше 10 минут Только 20% разработчиков во время деплоя переключаются на другие рабочие задачи. Остальные 80% ходят за чаем, общаются с семьёй и листают соцсети🙂

JUnit, часть 3: модели кастомизации Изменение архитектуры - не всё, чем JUnit 5 отличается от предыдущей версии. Второе отличие касается модели кастомизации. В этом посте поговорим, зачем это нужно в тестовом фреймворке, и про разницу между 4 и 5 версией. Чтобы было понятнее, давайте опишем простую задачу и будем её понемногу усложнять. Допустим, нужно измерить время выполнения каждого теста: запустить таймер вначале и вывести время выполнения в конце. Для одного класса это несложно - просто добавляем методы с аннотациями @Before и @After. А как посчитать время для всех классов? Здесь варианта два: 🔸 Вынести общий код в отдельный класс, в каждый класс-тест добавить методы Before и After. Решение рабочее, но придётся копипастить методы в каждый класс. 🔸 Внедрить логику где-то на верхнем уровне и включать/выключать её через настройки или аннотации. Это и есть кастомизация - предусмотренные библиотекой места "встраивания" новой логики. JUnit 4 и 5 используют для этого разные механизмы. Давайте кратко их обсудим. JUnit 4 Runner Переопределяем жизненный цикл теста целиком. Наследуемся от интерфейса Runner или абстрактного класса, в нужных местах добавляем нужные действия. Теперь тесты запускаются не по стандартной схеме, а по той, что прописана в новом классе. Примеры: ▫️ @RunWith(Parameterized.class) запускает параметризованные тесты ▫️ @RunWith(Suite.class) запускает наборы тестов ▫️ @RunWith(SpringJUnit4ClassRunner.class) добавляет спринговые активности до и после запуска теста ▫️ @RunWith(MockitoJUnitRunner.class) позволяет использовать заглушки Главный минус - жизненный цикл только один, значит Runner для теста может быть только один. Не получится совместить несколько фич, например, параметризованные тесты с заглушками. JUnit 4 Rule Переопределяем интерфейс TestRule и задаём действие до и после выполнения теста. В тестах выглядит как просто поле:
@Rule
public Timeout globalTimeout = Timeout.seconds(10);

В JUnit 4 есть несколько готовых правил: ▪️ TemporaryFolder - создать временную папку для теста ▪️ ExternalResource - открыть и закрыть внешний ресурс(файл, сокет, БД) Плюсы-минусы: ✅ Можно использовать несколько rule в одном классе ❌ Работает в рамках одного метода и по сути похож на before/after. JUnit 5 Extension Жизненный цикл теста разбивается на 10+ фаз. К каждой из них можно присоединиться, если переопределить нужный интерфейс: ▫️BeforeAllCallback - действие перед всеми тестами ▫️ParameterResolver - передача параметров в тест Реализуем нужные интерфейсы, регистрируем класс и готово. Похожий механизм используется в Spring. ✅ Класс может использовать несколько экстеншенов ✅ Можно вклиниться на любых этапах жизненного цикла ✅ В интерфейсах доступен контекст выполнения и вся информация про тесты, в итоге возможностей гораздо больше В JUnit 5 полностью убрали поддержку Runner и Rule, всё переписано на Extension API. Кодовые базы стали несовместимы между собой, поэтому и нужна библиотека Vintage с адаптерами. ______ Разбирать чужие кейсы полезно, но не всегда увлекательно. Поэтому вот интересный факт про разработку JUnit. JUnit - опенсорсный проект, где никто никому не платил за работу. Но рефакторинг назревал много лет. Однажды ребята решили, что такие грандиозные планы требуют фулл тайм и объявили краудфандинг на JUnit 5. Сумма требовалась небольшая - 25 тысяч евро, меньше двух миллионов рублей. В итоге собрали в 2 раза больше, и уже через 6 недель был готов первый прототип. Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе🙈

Как вы думаете, во сколько обошлась переделка архитектуры JUnit и написание практически новой кодовой базы?
Anonymous voting

JUnit, часть 2: зачем всё менять? В JUnit сложилась странная ситуация. JUnit 4 - топовый java фреймворк для тестов, который используется более чем в 100 тысячах проектов. JUnit 5 вышел спустя 10 лет и обещает много нового. Но нельзя просто взять и перейти на 5 версию: ▪️ Аннотации вообще другие ▪️ Зависимостей стало больше ▪️ Для совместимости с JUnit 4 надо подключать какой-то винтаж. Обратная совместимость - обычное дело для java приложений. В новой версии всё работает как раньше. Постепенно добавляешь новые фичи. Почему JUnit не пошёл по этому пути? Чтобы ответить на вопрос, нужно копнуть в архитектуру и кейсы использования. JUnit создавался как удобная библиотека для написания тестов. Для разработчика есть API - аннотации @Test, @Before, методы assertEquals и тд. Здесь всё классно. Дальше эти тесты запускает IDE или система сборки. И вот им приходится тяжело. В JUnit 4 API для запуска и анализа тестов очень ограниченный, поэтому IDE и сборщики используют рефлекшн и другие обходные пути. Чем плох такой подход - понятно. Любое изменение внутренней реализации ломает логику внутри IDE/системы сборки. JUnit 5 учёл эту проблему и содержит три отдельных артефакта: 🔸 Jupiter - апи для разработчиков 🔸 Platform - апи для запуска и анализа тестов. Целевая аудитория - IDE, плагины и системы сборки. Теперь каждый из них может использовать библиотеку, а не писать свой велосипед 🔸 Vintage - для запуска JUnit 4 тестов на новой платформе Почему у JUnit 4 и 5 разные аннотации? У JUnit 5 абсолютно другая кодовая база. Для совместимости с 4 версией пришлось бы наворотить много кода. Гораздо практичнее вынести все адаптеры в отдельный компонент. Тогда ▫️ Старые тесты будут работать ▫️ Чётко видно, где старые тесты, а где новые. А значит есть шанс, что со временем кодовая база с тестами перейдёт на новую версию. Что здесь особенного? В целом выглядит как обычный рефакторинг. Продукт развивается, мир меняется, монолит делится на составные части. Но в этой истории есть две важные детали. 1️⃣ На страничке принципов разработки команды JUnit есть такие строки: ▫️ JUnit has never tried to be a swiss army knife ▫️ Third party developers move more quickly than we do Отсюда видна ещё одна мотивация: поощрение развития других библиотек и фреймворков. Другие разработчики тестовых библиотек теперь могут использовать JUnit платформу и автоматически получать поддержку библиотек во всех IDE и системах сборки. 2️⃣ Вторая инициатива команды JUnit - проект Open Test Alliance for the JVM. В чём суть: есть много тестовых фреймворков и библиотек. Все они работают по-разному - бросают разные исключения, отличается формат и набор данных и тд. IDE и системам сборки приходится учитывать все особенности. Идея проекта - создать общую спецификацию для тестовых библиотек. Проект поддержали TestNG, Spock, Hamcrest, AssertJ, Eclipse, IntelliJ, Gradle, Maven и Allure. Неизвестно, закончится ли эта история удачно, но идея классная. Здорово, когда компания делает не только хороший продукт, но и способствует развитию отрасли в целом😇

JUnit, часть 1: джентельменский набор Список фич, которые пригодятся для большинства тестов. Если что-то не видели - почитайте, вдруг пригодится. У JUnit чудесная документация, так что подробно расписывать не буду. 1️⃣ Жизненный цикл теста Каждый тест - это метод с аннотацией @Test. Через аннотацию @DisplayName задаётся симпатичное имя теста в отчёте. Чтобы выполнить что-то до или после выполнения теста, используются методы с аннотациями
▫️ @Before, @BeforeAll
▫️ @After, @AfterAll

JUnit создаёт новый экземпляр класса на каждый тестовый метод. Класс ServiceTest с пятью методами @Test во время запуска превратится в 5 экземпляров класса ServiceTest. Благодаря этому тесты выполняются независимо. Этим JUnit отличается от TestNG, где создаётся один экземпляр класса на все тестовые методы. Если хочется как в TestNG, добавьте над классом аннотацию @TestInstance(Lifecycle.PER_CLASS) 2️⃣ Проверки Сердце каждого теста - методы с приставкой assert*:
🔸 assertTrue
🔸 assertEquals
🔸 assertInstanceOf

В самом JUnit мало методов, более удобные ассерты есть в библиотеках Hamсrest и AssertJ. AssertJ, на мой взгляд, более читабельный, но Hamсrest используется чаще. 3️⃣ Группировка тестов Аннотация @Tag("groupName") объединяет тесты в группы. Работает и для одного теста, и для класса. Можно указывать тэги в системе сборки и при запуске тестов из IDE. 4️⃣ Отключение тестов Аннотация @Disabled. Продвинутые варианты для: ▫️ операционной системы
@DisabledOnOs(WINDOWS)

▫️ версии java
@DisabledOnJre(JAVA_9)
@DisabledForJreRange(min = JAVA_9)

▫️ системных переменных:
@DisabledIfSystemProperty(named = "ci-server", matches = "true")
@DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*")

5️⃣ Параметризированные тесты Помогают запустить один тест с разными аргументами. Выглядит так:
@ParameterizedTest
@ValueSource(ints={100,-14})
public void test(int input) {}

Такой тест запустится дважды - с аргументом 100 и -14. Вместо готового списка можно брать значения 🔸 из CSV файла @CsvSource 🔸 из метода @MethodSource 6️⃣ Проверка таймаута ▫️ Через ассерт
assertTimeout(ofMinutes(2), ()->{});

▫️ Через аннотацию
@Timeout(value=42,unit=SECONDS)

7️⃣ Полезные библиотеки ▫️ Hamсrest, AssertJ - расширенные библиотеки методов-ассертов ▫️ Mockito для заглушек. Добавляете библиотеку в pom.xml или build.gradle, а в тест - аннотацию @ExtendWith(MockitoExtension.class) ▫️ Testcontainers для запуска внешних компонентов в докере. Добавляем библиотеку, аннотацию @Testcontainers над классом и @Container над компонентом ▫️ Java Faker - генератор данных для тестов