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 121 in the Technologies & Applications category and 52 862 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.
Ctrl+C и Ctrl+V, а настоящие шоумены - мультикаретки.
🔸 Добавляете каретку через Shift + Alt + Левый клик
🔸 Начинаете печатать
Вместо клика можно выделить часть текста и одновременно работать с несколькими фрагментами кода.
Чтобы вернуться в нормальный мир с одной кареткой, нажмите Esc.
Выглядит как магия, честное слово🔮🔸 ArrayBlockingQueue 🔸 LinkedBlockingQueue 🔸 ConcurrentLinkedQueueВ этом посте кратко расскажу, чем они отличаются и как выбрать подходящую. Часть 1: внутреннее устройство По названию класса легко предположить, что одна реализация сделана на основе массива, а две - с помощью связного списка. Что это даёт? По сути - ничего. Вспомним 2 списка с похожим строением - ArrayList и LinkedList. В первом легко искать элемент по индексу, во втором - вставлять и удалять элемент из середины списка. Для очереди это не важно, потому что нас интересует только работа с началом и концом очереди. Часть 2: механизм синхронизации А здесь уже интереснее: 🔹 ArrayBQ использует один ReentrantLock 🔹 LinkedBQ - два ReentrantLock: один для начала очереди, другой - для конца 🔹 ConcurrentLQ использует CAS операции для обновления крайних элементов Давайте попарно сравним их между собой ▪️ArrayBQ и LinkedBQ Отдельные локи для начала и конца очереди в LinkedBQ хорошо работают, когда начало очереди не совпадает с концом. То есть в очереди всегда что-то есть. С двумя локами с очередью в каждый момент времени могут работать два потока. Если размер колеблется около нуля, то поддерживать два лока слишком затратно, гораздо проще работать с одним локом на всю очередь. Это вариант ArrayBQ. Поэтому первый критерий, по которому выбираем экземпляр очереди: сколько элементов она содержит большую часть времени. ▪️LinkedBQ и ConcurrentLQ Допустим, наша очередь никогда не пустует. Что лучше - два ReentrantLock или две CAS операции? Здесь разница в методах, которые нам нужны. CAS операции обновляют только сами элементы - начало и конец очереди. ReentrantLock ограждает критическую секцию, внутри которой ▫️ Проверяется текущий размер и сама возможность добавить в очередь ▫️ Обновляется начало или конец очереди ▫️ Уведомляются потоки, которые, возможно ждут на другом конце очереди Добавление и удаление в ConcurrentLQ происходит очень быстро. Если нужны дополнительные опции - фиксированный размер, блокирующие вызовы, то лучше подойдёт LinkedBQ. Поэтому критерий выбора очереди №2: необходимые методы и ограничения на размер
return m > 10;Написали для него тесты: ▫️
m = 0, ожидаем false
▫️m = 100, ожидаем true
Фреймворк поменяет код на m ≥ 10, и ни один тест не упадёт. Значит, текущих тестов недостаточно, нужно проверить условие m = 10
Практика
Для JVM языков по факту есть только один фреймворк Pitest с двумя опциями - Gregor and Descartes.
Как работает:
1️⃣ Запускает тесты и анализирует test coverage. Если код не покрыт тестами, то нет смысла мутировать его.
2️⃣ Составляет список мутаций. Здесь два варианта:
🔸 Gregor тестирует по максимуму, в том числе геттеры и сеттеры
🔸 Descartes использует меньше вариаций, но работает быстрее
3️⃣ Для каждого пункта фреймворк меняет байт-код и запускает тесты. Все изменения, для которых тесты прошли, попадают в итоговый репорт
Минусы
❌ Проверяются только несложные изменения бизнес-логики
❌ Долго выполняется: от 10 минут до 6 часов. Для популярных библиотек результаты такие
❌ Сложно читать результаты и понять, каких тестов не хватает. Связано это с тем, что фреймворк работает не с исходным кодом, а с байт-кодом
❌ Gregor плохо совместим с Kotlin и проверяет конструкции, недопустимые на уровне языка. Приходится либо терпеть множество предупреждений, либо исключать некоторые проверки и рисковать бизнес-логикой. Либо использовать Descartes
❌ Слабо развивается и выглядит как pet project
❌ Непонятно как встроить в CI. Так как возможны ложноположительные результаты, репорты нужно проверять вручную. Можно оставить как необязательную опцию для самых мнительных, но проверка занимает много времени и вряд ли будет востребована
Итог: идея интересная и перспективная, но слабо развивается на практике.
Хозяйке на заметку
Подход mutation testing подойдёт для проверки тестов сложного или критичного участка кода:
▫️Поменяйте условие
▫️Верните константу в метод
▫️Удалите тело в void методе
▫️Поменяйте методы местами
▫️Уберите инкремент
▫️Верните null
▫️Поменяйте бизнес-логику
И запустите тесты. Если падают - значит код протестирован хорошо. Если нет - допишите тесты. Главное - вернуть потом изменения обратно🙂String
▫️sout
System.out.println();
▫️main
public static void main(String[] args){}
▫️prsf
private static final
🔸Сложные
Разворачиваются в методы с параметрами для автозаполнения. Перемещаться между полями можно через Tab:
▫️ fori
for (int i=0; i< ; i++) {}
▫️ifn
if (args == null) {}
▫️mx
Math.max(, );▫️lazy
if (obj == null)
{ obj = new Integer(); }
Полный список live templates: File/Settings/Editor/Live Templates.
Есть для Java, Kotlin, JS, Groovy, для разработки под Android и React.
2️⃣ Code completion
Дополнение имен на основе контекста.
🔸Начните набирать начало класса/метода:
▫️Int → Integer
▫️Cust → Customer
🔸Для классов наберите заглавные буквы:
▫️NPE → NullPointerException
▫️CHM → ConcurrentHashMap
🔸Добавьте синтаксическую конструкцию:
▫️count == 4.if
if (count == 4) {}
▫️list.for
for(Integer i : list) {}
▫️obj.opt
Optional.of(obj)▫️answer.switch
switch (answer) {}
Полный список: File/Settings/Editor/General/Postfix Completion. Есть варианты для Java, Kotlin и JS.Parent[] arr;🔸 Присваиваем массив Child:
arr = new Child[2];🔸 Добавляем в arr экземпляры Child и Parent:
arr[0] = new Child(); arr[1] = new Parent();Вторая строка в рантайме бросит ArrayStoreException. Дженерики обрабатываются при компиляции, поэтому ошибок на этапе выполнения нет. Wildcards Для ситуаций, когда мы точно-точно не будем менять список, и нам только посмотреть, можно использовать wildcards: void print(List<?> list) Теперь метод принимает списки любых типов. Конкретный тип неизвестен, поэтому доступны только методы класса Object. Можно получить размер списка или вызвать для элементов toString. ❓Чем List<?> отличается от List? В List<?> компилятор не даст ничего добавить, а для List ограничений нет. ❓Как вызвать нужные методы для List<?>? Тип в списке можно уточнить:
void print(List<? extends Parent> list)Теперь метод принимает только List<Parent> и List<Child>, плюс есть доступ к методам Parent. Но менять список всё ещё нельзя. Буквы и вопросики Есть два способа ограничить типы списков:
▫️ <T extends Parent> void test(List<T> list) ▫️ void test(List<? extends Parent> list)Чем они отличаются? T - это конкретный тип элементов списка, поэтому в список можно добавить объекты типа Т. В листе с вопросиком конкретный тип НЕ известен, и компилятор НЕ даст добавить новые элементы. extends и super В иерархии Object - Parent - Child можно ограничить типы с разных сторон. extends - ограничение иерархии "сверху"
? extends Parent: допустимы Parent, Child и наследники Child
super - ограничение типов "снизу"
? super Parent принимает Parent и родителей Parent вплоть до ObjectList values = new ArrayList()Чтобы нормально работать, приходилось использовать явное приведение:
values.add("value");
String s = (String) values.get(0);
Главная проблема с коллекциями тех лет - type loss, потеря типа при компиляции и написании кода:
❌ Можно запросто получить ClassCastException
❌ Надо помнить тип объектов
❌ Ошибки возникают только в рантайме
В java 5 появилась возможность указать тип через дженерики:
List<String> values = new ArrayList<String>();
values.add("p");
String v = values.get(0);
✅ Удобно пользоваться
✅ Хорошо читается
✅ Компилятор проверяет типы
Как реализованы дженерики?
Примерим на себя должность архитектора java и прикинем, что можно сделать с ArrayList<T>:
1️⃣ Скомпилировать в класс с типом String
Все T заменяем на String:
void add(String str) String get(int index)В итоге создаём отдельный класс для каждого типа данных. 2️⃣ Добавить в JVM параметризованный тип <T> остаётся на уровне байт-кода и определяется для каждого объекта в рантайме. 3️⃣ Оставить на уровне JVM работу с объектами. Проверить все типы во время компиляции и привести один к другому там, где нужно. Получаем один ArrayList.class и множество дополнительных конструкций по всему коду. Для ArrayList<String> программист напишет:
String value = list.get(0)Компилятор преобразует это в
String value = (String) list.get(0);🤔Все варианты адекватные и имеют право на жизнь. Что же выбрать? Почти в каждом IT офисе на стенах висят слоганы и ценности компании. Что-то про качество работы, развитие сотрудников, гибкость и довольных клиентов. Из этого получается классный корпоративный мерч. Ценности нормального человека определяют стратегию и дают ориентир для решения сложных ситуаций. "Ценности" java явно нигде не определены, но направление работы задано чётко. Например, кроссплатформенность и слоган Write Once, Run Anywhere заставляют разработчиков JDK поддерживать фичи даже для древних процессоров. Бинарная совместимость - ещё один ориентир java. Поэтому для дженериков используется третий вариант: компилятор добавляет необходимые приведения типов в код и стирает информацию о дженериках. И код на java 5 теперь совместим с предыдущими версиями. Ответ на вопрос перед постом В консоли напечатаются оба значения. У списка tmp тип не задан, поэтому компилятор не мешает добавлять строки и передавать объекты в метод println. Если в println передать список intList.get(0), где тип указан явно, то мы получим ошибку компиляции.
Available now! Telegram Research 2025 — the year's key insights 
