Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
Mostrar más📈 Análisis del canal de Telegram Java: fill the gaps
El canal Java: fill the gaps (@java_fillthegaps) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 12 549 suscriptores, ocupando la posición 10 121 en la categoría Tecnologías y Aplicaciones y el puesto 52 862 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 12 549 suscriptores.
Según los últimos datos del 07 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -46, y en las últimas 24 horas de 0, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 34.72%. Durante las primeras 24 horas tras publicar, el contenido suele obtener N/A% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 0 visualizaciones. En el primer día suele acumular 0 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 0.
- Intereses temáticos: El contenido se centra en temas clave como redis, hashmap, linkedhashmap, индекс, фича.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 08 junio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
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), где тип указан явно, то мы получим ошибку компиляции.
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
