Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
Show more📈 Analytical overview of Telegram channel Java: fill the gaps
Channel Java: fill the gaps (@java_fillthegaps) in the Russian language segment is an active participant. Currently, the community unites 12 549 subscribers, ranking 10 120 in the Technologies & Applications category and 52 841 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 12 549 subscribers.
According to the latest data from 07 June, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -46 over the last 30 days and by 0 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 34.72%. Within the first 24 hours after publication, content typically collects N/A% reactions from the total number of subscribers.
- Post reach: On average, each post receives 0 views. Within the first day, a publication typically gains 0 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 0.
- Thematic interests: Content is focused on key topics such as redis, hashmap, linkedhashmap, индекс, фича.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
Thanks to the high frequency of updates (latest data received on 08 June, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
▫️ READ_UNCOMMITED ▫️ READ_COMMITED ▫️ REPEATABLE_READ ▫️ SERIALIZABLEКаждый уровень гарантирует решение чёткого списка проблем. Для остальных нужно либо писать код, либо забить на возможные неточности. В стандарте SQL описаны три проблемы: 🔸 Dirty reads - грязные чтения Транзакция 1 обновляет поле Х. Другие транзакции видят новое значения Х до того, как транзакция 1 завершится. В вопросе с переводом денег как раз возникла такая ситуация. Транзакция перевода ещё не завершилась, а другая транзакция прочитала промежуточные значения. Где возникает проблема: только на уровне READ_UNCOMMITED. 🔸 Nonrepeatable reads - неповторяющиеся чтения Транзакция 2 читает поле X и работает с ним. В это время транзакция 3 обновляет поле Х. В итоге транзакция 2 работает с устаревшим значением. Более формально, "неповторяющееся чтение" - когда чтение одного поля в начале и конце транзакции даёт разные результаты. Но редко кто читает одно поле дважды, на практике получается либо бесполезная транзакция с устаревшими данными, либо несогласованные данные внутри транзакции. Проблема остро проявляется для долгих запросов - бэкапов или аналитики. Решается на уровне REPEATABLE_READ и выше. 🔸 Фантомные чтения Транзакция 3 проверяет условие по большому количеству записей. Транзакция 4 меняет выборку, например, добавляет новую запись. Если условие в транзакции 3 перестанет выполнятся, транзакция 3 этого не заметит. Важно, что условие касается не одного поля, а многих. В этом разница с неповторяющимся чтением - там меняется одно конкретное поле, а в фантомном чтении меняется вся выборка, по которому проверяется условие. Проблема решается на уровне SERIALIZABLE. Подробные примеры и схемы аномалий есть в Википедии. Я перечислила основные, но у многих проблем есть разновидности, поэтому аномалий получается больше, чем уровней изоляции. Каждая БД сама решает, какие проблемы решать на конкретных уровнях. У MS SQL Server - 5 уровней, у Oracle - 3. Большинство NoSQL баз не поддерживают транзакции, поэтому для них указывать тип изоляции бессмысленно. В универсальных адаптерах типа JDBC, Hibernate и Spring Data уровней столько, сколько в стандарте - 4. Ещё одна проблема, которой нет в SQL стандарте, но встречается на практике: 🔸 Потерянный апдейт Транзакции работают с одними данными и не учитывают друг друга. Пример: транзакция 5 и транзакция 6 одновременно прочитали значение счётчика. Каждая транзакция прибавила к значению единицу и обновила поле счётчика. Вначале они прочитали одно значение, и получается, что один инкремент потерялся. Проблема решается не только уровнями изоляции, но и SQL конструкциями: 🔹 Атомарный апдейт:
UPDATE test SET x=x-1 where id=1;🔹 Блокировка строки:
SELECT * FROM test WHERE id = 1 FOR UPDATE;Итог. Как учитывать внутрянку БД в написании кода: 1️⃣ Выбирать уровень изоляции с учётом вероятности и критичности проблем 2️⃣ Уточнить в документации БД, какие проблемы решает выбранный уровень 3️⃣ Писать код с учётом возможных аномалий 4️⃣ Помнить о потерянных апдейтах
price numeric, sale_price numeric CHECK(price>sale_price)Условие "обычная цена выше скидочной" задано в пределах ОДНОЙ записи. БД контролирует это условие и выдаст ошибку при несоблюдении. 🔸 Другое дело - транзакция, в которой меняются РАЗНЫЕ записи. Нельзя атомарно перевести 100 рублей с одного аккаунта на другой. Будет момент, когда с одного аккаунта уже сняли 100 рублей, а на другой ещё не перевели. Целостность данных ненадолго нарушится. От уровня изоляции зависит, заметят ли несоответствие другие транзакции. Эту тему обсудим сегодня.
public native int hashCode();В разных JVM реализации могут отличаться. Рассмотрим исходный код hashcode() в OpenJDK. Там 6(!) стратегий вычисления хэшкода. Стратегия задаётся опциями VM:
-XX:+UnlockExperimentalVMOptions -XX:hashCode={число}
При первом вызове результат сохраняется внутри объекта. Хэшкод для Object считается один раз и не меняется, даже если объект перемещается в памяти. Посмотрим все возможные варианты:
🔸 Стратегия по умолчанию.
Случайное число по алгоритму Xorshift RNG. Следующее значение вычисляется на основе предыдущего. Значения равномерно распределены. У каждого потока свой генератор. Синхронизации между потоками нет, поэтому алгоритм работает быстро.
🔸-XX:hashCode=0
Случайное число по алгоритму Lehmer RNG. Генератор один на все потоки, поэтому работает медленно.
🔸-XX:hashCode=1
Адрес объекта в памяти и немного манипуляций с битами. Работает быстро, но не даёт равномерного распределения хэш кодов.
🔸-XX:hashCode=2
Чемпион по скорости, возвращает 1 для всех объектов:
java.lang.Object@1Используется как отправная точка для тестов остальных стратегий. 🔸
-XX:hashCode=3
Обычная возрастающая последовательность:
java.lang.Object@a4 java.lang.Object@a5 java.lang.Object@a6🔸
-XX:hashCode=4
Текущий адрес в памяти. Популярный, но неправильный ответ на собеседованиях. Отчасти в этом виновата документация: там адрес приводится как пример реализации.
Рейтинг стратегий по скорости:
🏆 Вернуть единицу: 184 операций за микросекунду
🥈 Вариант по умолчанию: 176 оп/мск
🥉 Адрес в памяти-1: 175 оп/мск
4️⃣ Адрес в памяти-2: 160 оп/мск
5️⃣ Растущая последовательность: 14 оп/мск
6️⃣ Случайное число и глобальная переменная: 10 оп/мск
Интересно, что до java 8 самая медленная опция была вариантом по умолчанию.
Резюме:
✅ Реализация хэшкода зависит от JVM и VM-флажков.
✅ Расчёт на основе адреса памяти не даёт равномерного распределения.
✅ Общие переменные в маленьких методах снижают производительность в 10 раз.
✅ В OpenJDK 6 стратегий вычисления хэшкода. По умолчанию используется генератор случайных чисел в рамках одного потока.toList()
С 25 по 27 ноября была серия постов о коллекторах (часть 1, часть 2, часть 3). Там я писала, что вместо
collect(Collectors.toList())было бы удобно писать просто
toList()30 ноября разработчик Oracle добавил метод toList() в класс Stream. Вряд ли он читает этот канал, но совпадение интересное🙂 Новый метод не совсем равнозначный: ▫️Collectors.toList() возвращает экземпляр ArrayList. ▫️Новый метод toList() возвращает неизменяемый список. 2️⃣ mapMulti Это оптизированный flatMap. Объясню суть на примере. Заказ - класс Order, товар - класс Item. Заказ состоит из нескольких товаров. Из списка заказов хотим получить список всех товаров.
orders.stream() .flatMap(order->order.getItems().stream()) .toList();flatMap переводит "список списков" в один список. Товары для каждого заказа превращаются в Stream, а метод flatMap объединяет эти стримы в один. Минус: объект стрима создаётся всегда, даже для пустых списков. mapMulti устраняет этот недостаток:
orders.stream() .<Item>mapMulti((order, consumer) -> order.getItems().forEach(item -> consumer.accept(item)) ).toList();Что происходит: mapMulti принимает на вход (order, consumer): ▪️order - элемент стрима, в нашем случае - заказ ▪️consumer - следующий этап в стриме. Наша задача - передать этому этапу все будущие элементы. Берём у заказа товары и для каждого вызываем consumer.accept(item). Мультимэп не знает, для каких объектов будет вызван accept, и не может вывести тип выходных элементов. Поэтому для нормальной работы тип надо указать явно:
<Item>mapMulti(…)Когда использовать mapMulti? ✅ Небольшое количество элементов в списках. Например, много заказов с 1-2 товарами ✅ Элементы легко получить без Stream API Основная фишка mapMulti - нет промежуточных стримов. Если внутри метода создаётся стрим, то вся выгода сходит на нет. ❌ order.getItems().stream()… Есть три вариации метода: 🔸IntStream mapMultiToInt 🔸LongStream mapMultiToLong 🔸DoubleStream mapMultiToDouble Для них выходной тип не указывается.
.class
Deprecated d = Account.class. getAnnotation(Deprecated.class)🔸 Делаем что-то полезное 🔸 Включаем процессор в компиляцию. В maven-compiler-plugin это секция annotationProcessors. Для обработки подойдут аннотации с любой RetentionPolicy. Что получаем: ❌ Долгая компиляция ❌ Специфичное тестирование. Пример 😐 Сложный и запутанный код ✅ Не тратится время на старте приложений Этот подход используется в библиотеке Lombok, микрофреймворках Quarkus и Micronaut, в Android фреймворке Dagger. Библиотека Dekorate на основе аннотаций создаёт манифесты для Kubernetes и OpenShift. 3️⃣ Статический анализ. Алгоритм такой же - создать наследник от AbstractProcessor, добавить в процесс компиляции. Примеры: библиотека Google Error Prone ищет в коде ошибки, Hibernate Validator проверяет, что аннотации Hibernate корректно расставлены. 4️⃣ Работа с байт-кодом. Редкий случай. В байт-коде остаются аннотации с RetentionPolicy.CLASS или RUNTIME. 5️⃣ Создать объекты и прокси-классы. Доступно для аннотаций с RetentionPolicy.RUNTIME. Основа работы многих фреймворков: Spring, Hibernate, Java EE. Под работу с аннотациями в рантайме заточены многие библиотеки: Reflections, Spring. Работать с ними удобно и приятно. Обработка рантайм-аннотаций обычно происходит на старте приложения, поэтому запуск сервиса может занимать несколько минут. ❌Теперь рассмотрим анти-кейсы, для чего аннотации НЕ нужны: 1️⃣ Отметить код для себя или команды. Поставить аннотацию @RefactorASAP легко, но без дальнейших действий это бесполезно. Аннотации нужно обрабатывать, и делать это автоматически. Чтобы запомнить место в коде, используйте TODO комментарии в Intellij IDEA. 2️⃣ Для бизнес-логики. Аннотации легко добавить в обход ООП и основной логики. Так можно быстро решить проблему, но долгосрочно это неудачный вариант: ❌ Нельзя контролировать процесс целиком ❌ Сложно писать тесты ❌ Сложно дебажить ❌ Внезапные сайд-эффекты Частая ситуация на Spring проектах: на старте запускаются десятки @PostConstruct. Если в процессе возникает ошибка, то найти и исправить её непросто. Но вообще, чем меньше вы полагаетесь на аннотации, тем лучше.
@Override, @Deprecated, @SuppressWarnings - вот это всё.
Первая часть будет о том, как сделать свою аннотацию, а во второй расскажу, когда и зачем это нужно.
Создать аннотацию легко:
public @interface MyAnnotation {}
❓Почему ключевое слово @интерфейс, а не @аннотейшн?
Во времена java 4 аннотаций не было и для дополнительной информации классу добавляли интерфейс-маркер. В интерфейсах Cloneable, Serializable, Remote нет методов, они используются только как дополнительный признак класса.
Подход рабочий, но похож на костыль. Цель интерфейса - показать контракт класса, поэтому для маркировки кода в java 5 ввели аннотации.
Вернёмся в наши дни. Посмотрим исходный код @Deprecated:
@Retention(RUNTIME)
@Target(value={FIELD,…})
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
На этом примере видно, из чего состоит аннотация:
🔸Поля
Содержат доп. информацию. Если указать значение по умолчанию, поле становится необязательным:
@Deprecated(since="14")// forRemoval по умолчанию false 🔸Список внутри @Target показывает элементы, для которых работает аннотация. В java 7 аннотации доступны для классов, методов, параметров, полей и переменных. В java 8 аннотации действуют везде, где указан тип. Можно писать даже такое:
▫️new @Test Account() ▫️throws @Test IOException ▫️implements @Test Comparable<@Test
T>
Такие аннотации называются type annotations и используются в IDE и компиляторах для анализа и строгого контроля типов.
Аннотации нельзя ставить для имён переменных. Правильный ответ на вопрос перед постом - ошибка в 5 строке:
✅ @Test String doubled
❌ String @Test out
🔸@Retention определяет, когда доступна аннотация и как её можно использовать. Все виды подробно рассмотрим во второй части.
@Target и @Retention- это мета аннотации, то есть аннотации для аннотаций.
Какие ещё бывают мета-аннотации:
🔸@Documented - аннотация появится в JavaDoc
🔸@Inherited - наследуется подклассами
🔸@Repeatable (Java 8) - можно использовать несколько раз для одного элемента. Иногда такое приятнее читать, чем один массив:
@Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23")Создать аннотацию легко, правильно применить - уже сложнее. С этим вопросом разберёмся во второй части.
git push --force.
♊️ Близнецы
Год будет спокойным и приятным. В прошлом году вы много работали, в 2021 выделяйте больше времени на отдых. Качество жизни и работы только улучшится. Самое время взяться за большие и фундаментальные книги, которые вы долго откладывали.
♋️ Рак
Откажитесь от лишней эмоциональности, она может помешать вашему развитию. Подтяните DevOps, в этом году он вам пригодится. Сложные задачи ждут вас в середине лета, но они дадут нужный стимул для дальнейшего роста.
♌️ Лев
В этом году удача не на вашей стороне, придётся много работать. Хотите успеха — начните сейчас, чтобы уже весной видеть первые результаты. Смотрите на вещи шире. Почитайте книжки по архитектуре, посмотрите видео с конференций HighLoad и ArchDays. В апреле высокий риск простудиться, одевайтесь теплее.
♍️ Дева
Год будет богат на свежие идеи и начинания. Начните то, что давно откладывали. Интересные идеи, вопросы и решения придут в самый неожиданный момент. Сохраняйте их сразу - запишите в блокнот, голосовое сообщение, как угодно, а то улетят. Обязательно делайте бэкапы и резервные копии.
♎️ Весы
В этом году у Весов будет шанс попробовать себя в руководящей роли. Готовьтесь заранее - почитайте статьи по управлению людьми и проектами. Подтяните тайм-менеджмент, иначе времени на хобби и внерабочие активности совсем не останется.
♏️ Скорпион
В этом году вы будете на переднем фронте. Вас ждут горячие фиксы и спасение команды перед дедлайном. Будет сложно, но Сатурн вам поможет. Помните об отдыхе и набирайтесь сил в спокойное время.
♐️ Стрелец
Пересмотрите приоритеты в жизни, попробуйте смежные IT направления. Возможно позиция тимлида, менеджера или аналитика раскроют вас с новой стороны. В этом году особую важность приобретут межличностные отношения. Октябрь станет самым прибыльным месяцем в году.
♑️ Козерог
Для вас 2021 год — это борьба со своими слабостями. Уделяйте больше внимания тестированию и самопроверке. Разберитесь с NoSQL: книга для начинающих, для продолжающих. Хорошей идеей будет сходить на каток и покататься на ватрушках.
♒️ Водолей
Лучшее время для решительных шагов — начало весны. Много возможностей принесёт нетворкинг - поддерживайте тёплые отношения с коллегами, участвуйте в конференциях, митапах и корпоративных мероприятиях. Идите в ногу со временем - освойте Kotlin и Cloud computing.
♓️ Рыбы
Наступает период, когда пора применить все накопленные знания. А возможности для этого обязательно будут. Меркурий помешает сделать важные задачи в срок, поэтому закладывайте на выполнение в 2 раза больше времени. Лето подкинет массу интересных вариантов для отдыха.
Дружите со звёздами, они плохого не посоветуют💫switch (user.getState()) {
case NEW: …
case CONFIRMED: …
case BANNED: … }
Switch - это не просто набор нескольких if. У объекта user 3 статуса. Список чётко определен, статусы не пересекаются между собой.
Почему switch так себе вариант?
❌Сложный код. Работа со всеми состояниями в одной куче.
❌Дублирование кода. Если поле проверяется несколько раз, то менять такой код неудобно и легко ошибиться.
Для объекта с понятным набором состояний switch лучше заменить на полиморфные методы. Это несложный рефакторинг - пример1, пример2.
Цель ООП - смоделировать реальный мир через объекты. Главное здесь - объекты взаимодействуют и меняют состояние друг друга. В таких условиях switch проигрывает полиморфным методам и редко используется.
2️⃣ Часть 2. Эра функциональности
Сегодня для бизнеса недостаточно простой автоматизации. Основной задачей становится работа с данными.
Они не меняются, приходят из разных источников в разных форматах. Потоки данных идут через множество сервисов. Каждый сервис берёт данные, которые понимает, а остальные игнорирует. Строить иерархии классов для такой задачи кажется лишним и сложным.
Поэтому внедряются подходы из фунциональных языков. Один из них - pattern matching, а switch идеально подходит для его реализации. Паттерн - некоторое условие для переменной:
▫️Равна заданной константе
▫️Имеет определённый тип
▫️Подходит под регулярное выражение
Если произошёл мэтч, то для переменной сразу доступна доп.информация. Например, она приводится к нужному типу:
switch (animal) {
case Cat c → c.putToBox();
case Dog d → d.train(); }
Итак, в чём разница между switch в 2000 и switch в 2021?
Switch 2000 работает с объектами, у которых меняется состояние. Вокруг этого строится бизнес-логика.
Switch 2021 работает с неизменными данными и помогает найти среди них подходящие. В следующем году выйдет java 17, и switch будет появляться в коде чаще.
Вот так один непопулярный оператор в JDK нашёл своё место в мире спустя 23 года⭐️
Available now! Telegram Research 2025 — the year's key insights 
