Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 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),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
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), где тип указан явно, то мы получим ошибку компиляции.
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
