uz
Feedback
Java: fill the gaps

Java: fill the gaps

Kanalga Telegram’da o‘tish

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

Ko'proq ko'rsatish

📈 Telegram kanali Java: fill the gaps analitikasi

Java: fill the gaps (@java_fillthegaps) Rus til segmentidagi kanali faol ishtirokchi. Hozirda hamjamiyat 12 549 obunachidan iborat bo'lib, Texnologiyalar & Aralashmalar toifasida 10 121-o'rinni va Rossiya mintaqasida 52 862-o'rinni egallagan.

📊 Auditoriya ko‘rsatkichlari va dinamika

невідомо sanasidan buyon loyiha tez o‘sib, 12 549 obunachiga ega bo‘ldi.

07 Iyun, 2026 dagi oxirgi ma’lumotlarga ko‘ra kanal barqaror faollikka ega. Oxirgi 30 kunda obunachilar soni -46 ga, so‘nggi 24 soatda esa 0 ga o‘zgardi va umumiy qamrov yuqori darajada qolmoqda.

  • Tasdiqlash holati: Tasdiqlanmagan
  • Jalb etish (ER): Auditoriya o‘rtacha 34.72% darajada jalb etiladi. Nashrdan keyingi dastlabki 24 soatda kontent odatda umumiy obunachilar sonining N/A% ini tashkil etuvchi reaksiyalarni to‘playdi.
  • Post qamrovi: Har bir post o‘rtacha 0 marta ko‘riladi; birinchi sutkada odatda 0 ta ko‘rish yig‘iladi.
  • Reaksiyalar va o‘zaro ta’sir: Auditoriya faol: har bir postga o‘rtacha 0 ta reaksiya keladi.
  • Tematik yo‘nalishlar: Kontent redis, hashmap, linkedhashmap, индекс, фича kabi asosiy mavzularga jamlangan.

📝 Tavsif va kontent siyosati

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

Yuqori yangilanish chastotasi (oxirgi ma’lumot 08 Iyun, 2026 da olingan) sababli kanal doimo dolzarb va katta qamrovli bo‘lib qoladi. Analitika auditoriya kontent bilan faol hamkorlik qilishini, uni Texnologiyalar & Aralashmalar toifasidagi muhim ta’sir nuqtasiga aylantirishini ko‘rsatadi.

12 549
Obunachilar
Ma'lumot yo'q24 soatlar
-247 kunlar
-4630 kunlar
Postlar arxiv
Два доклада с 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 - генератор данных для тестов