Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
显示更多📈 Telegram 频道 Java: fill the gaps 的分析概览
频道 Java: fill the gaps (@java_fillthegaps) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 12 548 名订阅者,在 技术与应用 类别中位列第 10 113,并在 俄罗斯 地区排名第 52 819 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 12 548 名订阅者。
根据 08 六月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -43,过去 24 小时变化为 -3,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 34.73%。内容发布后 24 小时内通常能获得 N/A% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 0 次浏览,首日通常累积 0 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 0。
- 主题关注点: 内容集中在 redis, hashmap, linkedhashmap, индекс, фича 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
凭借高频更新(最新数据采集于 09 六月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
// givenИнициализация переменных и моков.
// whenВызов проверяемого метода.
// thenПроверка результата и состояния других объектов. 3 блока должны чётко следовать друг за другом, тогда тест легко читать. Если после блока проверок идёт действие и дополнительные проверки — значит тест слишком большой. 2️⃣ Что такое TDD? Test Driven Development - процесс разработки, когда сначала пишется тест, а потом код. Подробно это выглядит так: ▫️Написать тест для кода, которого нет ▫️Запустить тесты, новый тест падает ▫️Написать код, чтобы тест проходил ▫️Запустить тесты, тесты проходят ▫️Рефакторинг нового кода ▫️Запустить тесты, тесты проходят Плюсы TDD: ➕Код сразу покрыт тестами ➕У класса удобный интерфейс, который сразу используется в тестах ➕Структурированный код После этого следует такой диалог: - А Вы лично используете TDD? - Нет. - Почему? У него же столько плюсов. - Большой минус - частое переключение контекста и фокус на деталях. Мне удобнее написать простое рабочее решение, и только потом добавить проверки, валидацию и обработку ошибок. После этого в соответствии с требованиями как следует оттестировать то, что получилось. Я сразу пишу код, который легко использовать и тестировать, поэтому TDD меня только замедляет. - Согласен.
class Программист {
void написатьКод()
void помытьПосуду()
void погладитьКота()
}
Идеальная сущность с точки зрения Cohesion - лямбда-выражение. Их легко читать, тестировать и переиспользовать.
2️⃣ Связанность (Coupling)
Показывает, как одна сущность взаимодействует с другими. Класс А связан с классом Б, если в классе А
▫️Поля класса Б
▫️Вызов методов класса Б
▫️Локальные переменные класса Б
▫️Если класс А - подкласс Б
В большинстве проектов есть модуль base или common - часто он является примером сильной связанности. Менять такой модуль - всегда большой риск.
3️⃣ Сложность (Complexity)
На этот показатель влияет:
▪️Сколько сущностей взаимодействуют
▪️Количество операций в методе
▪️Глубина наследования
▪️Количество ветвлений и возможных состояний
Показатель сложности часто идёт рука об руку с размером, но не всегда. Сложный метод может быть небольшим по размеру, но скрывать внутри себя десятки вариантов развития событий:
public void check(long id) {
dao.get(id).findProcessor()
.getAccount().checkBalance();
}
4️⃣ Размер (Size)
Большие сущности сложно поддерживать. В этой категории много метрик, например, количество:
🔹Полей
🔹Методов
🔹Статических методов
🔹Подклассов
🔹Связанных библиотек
А теперь ответы на вопросы перед постом:
✅ Если класс А часто использует класс Б, то это признак высокой связанности. Связанность (coupling) характеризует внешние отношения между сущностями. Связность (cohesion) - про внутреннюю структуру самой сущности.
✅ На сложность влияет количество ветвлений и глубина наследования. Остальные характеристики относятся к категории "размер".// TODO добавить тестыТакие комментариии будут отображаться в окне TODO внизу экрана. Через него можно перейти в нужное место кода в один клик. Открыть список можно через ▫️Alt+6 ▫️View→Tool Windows→TODO Помимо стандартных TODO и FIXME можно добавить свои метки, например, OPTIMIZE, ASK, TEST. Сделать это можно в File →Settings→ Editor→ TODO.
Runnable r=() -> …
❌ Runnable r=new Runnable{
@Override
public void run(){…}
}
2️⃣ Определите сценарии использования ФИ.
ФИ — это интерфейс, но с одним абстрактным методом. В него можно добавить сколько угодно методов с реализацией — дефолтных, приватных и статических. Один ФИ может расширять другой ФИ. Класс может реализовать ФИ через implements.
Чтобы код был простым и понятным, нужно разграничивать его компоненты:
🔸Цель обычного интерфейса - обозначить набор методов для будущих классов.
🔸Цель ФИ — передать набор инструкций через лямбды и ссылки на методы.
Один метод в ФИ - оптимальный вариант. Но есть случаи, когда дополнительные методы уместны, так как тесно связаны с самим интерфейсом.
Пример: интерфейс Predicate. Несмотря на то, что он функциональный, в нём есть 4 метода с заданной реализацией: and, or, isEqual и negate.
3️⃣ Используйте аннотацию FunctionalInterface.
Она не обязательна, но облегчает чтение кода.
4️⃣ Если используете ФИ как аргумент, отражайте тип ФИ в названии метода.
Пример: в классе 2 метода с одним именем и разными типами аргументов:
void adapt(Callable) void adapt(Supplier)Этот код компилируется, но у него 2 недостатка: ▪️Чтобы понять разницу между методами нужно читать код или документацию. ▪️В качестве аргумента нельзя передать лямбда-выражение, т.к оно подходит под оба типа:
❌ adapt(str->print(str))Чтобы избежать этих проблем, используйте разные имена для методов. Хороший пример — класс CompletableFuture для многоэтапных вычислений. В его методах легко ориентироваться благодаря удачным названиям:
▫️thenAccept(Consumer)
▫️thenApply(Function)
▫️thenRun(Runnable)@FunctionalInterface
public interface Archiver{
void archive(String file)
throws FileNotFoundEx;
}
Экземплярами ФИ могут быть:
🔸Лямбда-выражение
🔸Ссылка на метод
🔸Ссылка на конструктор
Для большинства задач подойдут стандартные интерфейсы из пакета java.util.function. Всего их 43, но можно выделить 5 групп:
1️⃣ Consumer (потребитель)
Метод принимает один параметр и ничего не возвращает.
Consumer<String> c = System.out::println;2️⃣ Supplier (поставщик) Ничего не принимает, но возвращает значение.
Supplier<List> s = ArrayList::new;3️⃣ Function Принимает один параметр, возвращает одно значение:
Function<String,Long> map = s -> Long.valueOf(s);4️⃣ UnaryOperator Метод принимает один параметр и возвращает значение того же типа:
UnaryOperator<Integer> inc = i -> i+1;5️⃣ Predicate Принимает аргумент и возвращает boolean значение.
Predicate<String> filter =
s -> !s.isEmpty()
&& !s.contains("q");
У этих ФИ есть удобные вариации:
🔹BiConsumer, BiFunction, BiPredicate, BinaryOperator принимают два аргумента:
BiConsumer<Integer,Integer> b = (x,y) -> println(x+y);🔹LongFunction, LongSupplier , ToLongFunction и т.д позволяют не прописывать типы аргументов или выходных значений:
LongFunction<String> to = Long::toString;Ответ на вопрос перед постом: Интерфейс становится функциональным если в нём есть один абстрактный метод. Все остальные свойства у него такие же, как у обычного интерфейса. Аннотация FunctionalInterface не обязательна, но желательна для понимания кода.
interface Adapter {
default int get() {…}
}
Класс, который реализует интерфейс, переопределяет такой метод при необходимости.
❓Зачем нужны такие методы?
1️⃣ Облегчить изменения API
Во времена java 7 интерфейсы содержали только определения методов:
interface Collection<Т> {
void add(Т);
Т get();
}
Чтобы добавить, удалить или поменять сигнатуру метода нужно одновременно поменять код со стороны пользователей интерфейса и во всех реализациях. В рамках одного проекта это несложно, но для библиотек это сложная задача. Пользователи могут столкнутся с проблемами совместимости при переходе на новую версию.
Задача дефолтных методов - сгладить этот процесс и предоставить приемлемую или временную альтернативу.
2️⃣ Вспомогательные методы
Те методы, которые не входят в прямую функциональность интерфейса - методы для мониторинга, логгирования и т.д. Используйте с осторожностью, этот приём может нарушать принцип Interface segregation.
3️⃣ Комбинации базовых методов
В интерфейсе объявляется минимально необходимый набор методов, а некоторые методы являются просто их комбинациями. Мы обсуждали этот случай в прошлом посте. Такой "удобный" метод можно добавить в интерфейс как статический.
Статические методы вызываются через имя интерфейса и недоступны у экземпляров. Если это неудобно, оформите метод как метод по умолчанию.
Пример: интерфейс Comparator.
Основная функция — сравнение объектов через метод compare, индивидуальный для каждого класса. Цепочка из 2 компараторов - это 2 вызова compare и объединение результатов. Логика всегда одинакова, и нет смысла дублировать её в каждом подклассе:
default Comparator<T> thenComparing(Comparator) {…};
❓Что если класс реализует 2 интерфейса с методами по умолчанию?
Для разрешения конфликта используются 2 правила:
1️⃣ Если в классе переопределён метод по умолчанию — используется метод класса.
2️⃣ В иерархии интерфейсов используется метод наследника:
interface Child extends ParentЕсли класс реализует интерфейс Child, то будет использоваться дефолтный метод интерфейса Child. В опросе перед постом ни одно правило не подходит, поэтому будет ошибка компиляции. Чтобы разрешить конфликт, класс должен явно реализовать метод get();
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
