Java: fill the gaps
12.9K subscribers
7 photos
215 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Статический анализ кода, часть 1. Основные метрики

Главная цель принципов разработки и паттернов проектирования — снизить стоимость разработки. Хорошим считается код, который легко:
🔸читать
🔸менять
🔸тестировать
🔸переиспользовать

Простота - субъективное понятие. Разработчику, который в проекте со дня основания, легко ориентироваться в коде и исправлять ошибки. Джуниор, который недавно пришёл и получил первое задание, вряд ли с ним согласится. К счастью, есть объективные показатели качества кода, которые можно отслеживать и анализировать. Их получают с помощью статического анализа на основе исходного и байт-кода. Базовых метрик больше 50, и обычно они группируются в 4 категории:

1️⃣ Связность (Cohesion)
Показывает, насколько сильно внутренние элементы взаимодействуют друг с другом, насколько сфокусирован класс.
В классе с низкой связностью много методов, которые мало взаимодействуют между собой:
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) - про внутреннюю структуру самой сущности.
На сложность влияет количество ветвлений и глубина наследования. Остальные характеристики относятся к категории "размер".
👍3
Статический анализ кода, часть 2. Инструменты

В части 1 обсудили основные метрики: связность, связанность, сложность и размер. Также статический анализ включает проверку форматирования, поиск уязвимостей и возможных ошибок.

Когда и чем можно выполнять статистический анализ?
1️⃣ Во время написания кода.
С помощью встроенных правил IDE и дополнительных плагинов:
▫️Checkstyle проверяет форматирование, возможные ошибки, показывает сложные классы. Самый популярный плагин для Intellij IDEA.
▫️SonarLint и FindBugs ищут возможные ошибки, уязвимости, проверяют код на несколько тысяч ошибочных паттернов.
▫️CodeMR замеряет атрибуты качества кода — связность, сложность и т.д. Считает метрики, рисует картинки, показывает проблемные места с точки зрения дизайна.
▫️PMD ищет ошибочные паттерны в разных категориях — дизайн, кодстайл, производительность. Не знаю, почему этот плагин попадает во все списки анализаторов, у него достаточно скудный список правил по сравнению с аналогами.

2️⃣ Во время сборки.
Анализ редко делают в процессе сборки, но такая опция доступна — с помощью maven или gradle плагинов: Checkstyle Plugin, PMD Plugin.

3️⃣ На этапах CI/CD.
Можно запускать периодически или после определённых событий: создание пул-реквеста, слияние ветки. Результаты могут иметь рекомендательный характер, а могут не пропускать дальше по CI.

Конкретных инструментов много, как платных, так и бесплатных. Самый популярный - SonarQube. Метрики качества не считает, но проверяет на гигантское количество ошибочных паттернов и интегрируется с плагином в IDE.
JArchitect менее распространён и стоит дороже. Но и функций больше — метрики качества и красивые графики.

Статический анализ проводят в большинстве проектов. Обычно это проверка форматирования и поиск частых ошибок. Конкретные метрики считаются редко. И зря, ведь по ним можно объективно оценить:
🔸Текущее состояние кода
🔸Развитие системы от релиза к релизу
🔸Необходимость рефакторинга
🔸Проблемные места системы
1
Тестирование, часть 1. Вопросы на собеседовании

Чем опытней разработчик, тем больше тем обсуждается на собеседованиях. Начиная с уровня middle будьте готовы к таким вопросам:

1️⃣ Назовите свойства юнит-тестов?
Хорошие юнит-тесты соблюдают принцип FIRST:

🔸F – Fast
Выполняются быстро. Никто не хочет лишний раз запускать медленные тесты. Один юнит-тест — не дольше 0.5 сек.

🔸I – Isolated / Independent
Любой набор тестов можно запускать в любом порядке, тесты не зависят друг на друга. Каждый тест сфокусирован на одном действии и наборе параметров. Если тест упал, то сразу понятно, где и при каких условиях возникла ошибка.

🔸R – Repeatable
Результат теста не зависит от операционной системы, конфигурации компьютера, внешних ресурсов и наличия интернета. Результат теста повторяется при многократном вызове.
⭐️ Это свойство хромает, когда тесты используют случайные числа или проверяют работу нескольких потоков.

🔸S – Self-Validating
У теста два результата — прошёл или нет. Разработчик не должен проверять записи в логе или сравнивать текстовые файлы.

🔸T - Timely
Тесты пишутся вовремя, в рамках той же задачи, что и основной код.

⭐️ Юнит-тесты должны быть читаемыми. Тогда их легко поменять, если бизнес-логика изменится. Сложно разбираться в методах test1(), test2(), test3() и переменных a, b, c.
⭐️ По тестам можно оценить инженерную культуру компании. Обратите внимание на количество и качество юнит-тестов, кто и когда их пишет. Если тестов нет, или сеньор пишет код и создаёт подзадачу «написать тесты» для джуниора - это дно. Откладывать тесты из-за дедлайна — тоже так себе.
⭐️ Тесты можно не писать для PoC (proof of concept) — временный код для проверки гипотез, который не связан с продакшн кодом.

Тесты организуют по структуре 3A (Arrange-Act-Assert), также встречается название Given-When-Then.
// given
Инициализация переменных и моков.
// when
Вызов проверяемого метода.
// then
Проверка результата и состояния других объектов.

3 блока должны чётко следовать друг за другом, тогда тест легко читать. Если после блока проверок идёт действие и дополнительные проверки — значит тест слишком большой.

2️⃣ Что такое TDD?
Test Driven Development - процесс разработки, когда сначала пишется тест, а потом код. Подробно это выглядит так:
▫️Написать тест для кода, которого нет
▫️Запустить тесты, новый тест падает
▫️Написать код, чтобы тест проходил
▫️Запустить тесты, тесты проходят
▫️Рефакторинг нового кода
▫️Запустить тесты, тесты проходят

Плюсы TDD:
Код сразу покрыт тестами
У класса удобный интерфейс, который сразу используется в тестах
Структурированный код

После этого следует такой диалог:
- А Вы лично используете TDD?
- Нет.
- Почему? У него же столько плюсов.
- Большой минус - частое переключение контекста и фокус на деталях. Мне удобнее написать простое рабочее решение, и только потом добавить проверки, валидацию и обработку ошибок. После этого в соответствии с требованиями как следует оттестировать то, что получилось. Я сразу пишу код, который легко использовать и тестировать, поэтому TDD меня только замедляет.
- Согласен.
3👍2
Тестирование, часть 2. Тестовое покрытие

Code coverage (CC) - метрика, которая показывает, насколько код покрыт тестами.

Как считается СС?
Запускаются тесты и отслеживается, какие классы, методы и строки кода выполнились. Это число делится на общее количество и так получается процент кода, покрытый тестами.

Почему у СС плохая репутация?
Не говорит о качестве тестирования и надёжности системы. Если метод выполнился, это не значит, что он протестирован.
Тестировать get/set методы, конструкторы и классы-конфиги нет необходимости, но из-за жёстких требований по СС приходится писать бесполезные тесты.

Зачем считать СС?
Менеджеры хотят быть в курсе состояния проекта и не хотят смотреть в код. Поэтому их главный инструмент - это метрики, в том числе СС. Так оценивается текущая ситуация и сравниваются релизы.
Качество и надёжность— понятия субъективные. С помощью СС и последующего анализа можно найти компоненты, которые слабо покрыты тестами. Таким образом увеличить надёжность системы.

Какие значения СС нормальные?
Для большинства проектов это 60-80%.

Как посчитать?
1️⃣ Intellij IDEA:
Run → Run with Coverage
По умолчанию IDEA использует свой способ подсчёта, но для java есть ещё 2 библиотеки:
🔸JaCoCo
🔸Emma (не поддерживается с 2013)

Результаты разных библиотек могут отличаться. Если на этапе CI/CD используется JaCoCo, то лучше в IDE тоже использовать JaCoCo. Чтобы поменять:
Run → Edit Configurations... → Code Coverage → Choose coverage runner

2️⃣ Во время сборки.
Те же библиотеки используются в Maven и Gradle плагинах — JaCoCo Plugin, Emma Plugin.

3️⃣ На этапах CI/CD.
JaCoCo Plugin и Emma Plugin доступны для Jenkins, Teamcity, GitLab, Travis и остальных CI серверов.
3👍1
Статистика: что использовали Java разработчики в 2019 году

Бостонская компания Snyk каждый год опрашивает разработчиков об их проектах и технологиях. В прошлом году в опросе поучаствовало две тысячи человек, в том числе 8% участников из России.

Главное из отчёта за 2019:
1️⃣ Какая версия java используется в продакшене?
64% — 8
25% — 11
3% — 7 и ниже

2️⃣ Почему Вы не переходите на последнюю версию java?
51% — И так всё устраивает
32% — Трудности с миграцией
30% — Сложно согласовать переход

3️⃣ Какой JVM язык используется в качестве основного?
87% — Java
5,5% — Kotlin
2,9% — Closure
2,6% — Scala
1,5% — Groovy

4️⃣ Вы используете разбиение на модули (фича Java 9)?
64% — Нет
29% — Планируем
7% — Да

5️⃣ Какая у Вас версия Spring в продакшене?
48% — 5.1
18% — 5.0
17% — 4.3
5% — 4.2
4% — 3.2

6️⃣ Какой серверный веб-фреймворк Вы используете?
50% — Spring Boot
31% — Spring MVC
11% — JSF
5% — Vert.x
5% — Vaadin
5% — Struts

7️⃣ Ваша основная IDE:
62% — Intellij IDEA
20% — Eclipse
10% — NetBeans

8️⃣ Ваш инструмент сборки:
64% — Maven
25% — Gradle
6% — Ant

9️⃣ Ваш CI-сервер:
58% — Jenkins
12% — Никакой
6% — GitLab
5% — TeamCity
4% — Bamboo
4% — Travis
4%— Circle

🔟 Ваш репозиторий:
24% — private GitLab
16% — public GitHub
15% — Enterprise GitHub
15% — BitBucket Server
11% — public GitLab
10% — BitBucket Cloud

Полная версия отчёта тут. Очень удивило, что 12% не используют CI, и что Closure обогнал Scala. Рост популярности Intellij IDEA по сравнению с прошлым годом тоже впечатляет:
1
Сколько часов в день Вы пишете код?
Anonymous Poll
20%
Меньше часа
31%
1-3 часа
27%
3-5 часов
16%
5-8 часов
6%
Больше 8 часов
WakaTime: лёгкий способ прокачать навык эстимации

Секрет успеха прост — составить план и придерживаться его. Задача разработчика при планировании - оценить время на задачу с учётом сложности и рисков. Точные прогнозы это плюс к репутации, сорванные сроки — минус.

Навык оценки легко тренировать - почаще сравнивать оценку и фактическое время выполнения. Со временем у вас разовьётся сильная интуиция и адекватная оценка своих способностей. Приятный бонус - любовь менеджеров и низкий уровень стресса😌

Самое сложное — посчитать, сколько времени по факту ушло на задачу. Можно полагаться на память, а можно трекать время автоматически. Самый популярный плагин Intellij IDEA для Git — WakaTime. В бесплатной версии недоступно почти всё, но можно смотреть:
🔸Сколько времени ушло на написание кода
🔸Сколько времени заняла каждая задача:
5h 6 mins feature/EX-45
50 mins bugfix/EX-64

Для мониторинга своей активности этого достаточно.

Полная инструкция:
1️⃣ Установить в IDEA плагин WakaTime
2️⃣ Перезапустить IDEA, появится окошко для ключа
3️⃣ Зарегистрироваться на сайте и перейти в раздел для IDEA
4️⃣ Взять из п.5 API Key, ввести его в IDE
5️⃣ Зайти в настройки, отключить все уведомления
6️⃣ Зайти в дашборд

Можно трекать работу в других IDE и редакторах — Eclipse, Android Studio, GoLand, даже в Notepad++. Можно подключить к GitLab, GitHub и BitBucket и смотреть, сколько времени занял каждый коммит.
1
Привет!

Сегодня будет не полезный пост, а небольшая просьба. Я пишу посты в этом канале уже 5 месяцев: делюсь знаниями, инструментами и аналитикой, стараюсь доступно передать свой 7-летний опыт java разработки.

Постоянная обратная связь мотивирует писать дальше: меня очень радуют ваши лайки, шеры и личные сообщения. Приятно, что вы цените точную и практическую информацию и мой труд🧡

Я уже несколько месяцев занимаюсь созданием курса по многопоточности в java. В нём будет:
▫️Необходимая теоретическая часть
▫️Как и какими инструментами решать конкретные задачи
▫️Что актуально сейчас и тренды ближайших лет
▫️Разбор вопросов с собеседований

Большая часть материалов готова, но я хочу убедиться, что раскрыла все важные и интересные темы. Поэтому прошу вас мне помочь и заполнить небольшую анкету:
https://docs.google.com/forms/d/10zfRjnM6Fbpr03LWbNRETQDl3SNd7kjGmVh7Rh2XFRY/edit?usp=sharing
1
Ребята, спасибо, что уделили время заполнению анкеты! Я получила больше сотни ответов и ценных идей, буду внедрять💪
Многие писали, что присоединились недавно, поэтому на этой неделе будут подборки самых популярных статей. Если что-то пропустили — рекомендую прочесть.
Что напечатается в консоли?
Anonymous Poll
19%
true
78%
false
3%
Compilation error
Как JVM работает со строками, часть 1

По статистике OpenJDK в приложениях 15-50% памяти занимают экземпляры String. Это много, поэтому разработчики JVM прикладывают много усилий, чтобы оптимизировать работу со строками.

Как сэкономить память, если в системе миллионы экземпляров?

1️⃣ Уменьшить количество памяти под одну строку.

Мы уже разбирали этот случай: в java 9 вышло обновление Compact String и размер памяти под строки сократился до 2 раз.

2️⃣ Добавить кэширование.

Есть два способа создать экземпляр String:
🔸Через оператор new:
String s = new String ("one");
В памяти создаётся новый объект.

🔸Без оператора new:
String s = "one";

В этом случае идёт работа с кэшем строк под названием String pool. Схема работы такая:
▫️Проверить в String Pool, есть ли там такая строка.
▫️Если есть — вернуть её.
▫️Если нет — создать объект, поместить в кэш, вернуть.

Как реализован String pool?
Структура похожа на HashMap: хэш-таблица фиксированного размера с парами хэш — строка. В последних версиях java она занимает 64 МБ, при желании размер меняется с помощью флажка:
-XX:StringTableSize=65536

В следующем посте рассмотрим ещё два способа снижения издержек на строки.

Ответ на 1 вопрос перед постом:
new String("Java") создаёт новый объект в хипе,
s2 = "Java" создаёт объект в String pool. Это два разных объекта, поэтому при сравнении через == результат будет false.

Можно исправить ситуацию и добавить строчку в кэш методом intern():
String s1="Java";
String s2=new String("Java");
s2=s2.intern();
println(s1==s2); //true

Рассмотрим второй вопрос. О том, сколько строк создаётся в конструкции
String str=new String("Java");

Подвох в том, что для конструктора String нужен экземпляр String, поэтому создаётся 2 строки — "Java" и new String;

Но если строка "Java" использовалась раньше и попала в кэш, то будет создана только одна строка — new String.
1
А вот и второй вопрос, он должен был быть перед постом🤫

Сколько экземпляров String создаётся в этой строке кода:
String str = new String("Java");
Anonymous Poll
37%
1
61%
2
2%
3
Как JVM работает со строками, часть 2

Ссылка на часть 1.
Сегодня рассмотрим ещё 2 простых, но эффективных приёма JVM по оптимизации строк.

Оба связаны со внутренним устройством String:
byte[] value — текст
byte coder — тип кодировки
int hash

1️⃣ String Deduplication (Java 8)
При использовании оператора new в памяти создаётся новый объект:
String str = new String("12");

Отдельный JVM поток ищет в памяти экземпляры String и записывает их текст в хэш-таблицу:
hash → byte[]

Если поток находит дубликат, то заменяет ссылку в повторяющейся строке на ссылку из хэш-таблицы:
str.value = hashtable.value

На текст дубликата больше нет ссылок, поэтому он удалится сборщиком мусора. Так можно сэкономить 5-30% памяти. Опция String Deduplication работает только со сборщиком G1, и по умолчанию отключена.

Почему заменяется только текст, а не вся строка?
Ссылки на строчку могут быть в десятках мест. Текст — внутреннее поле, его заменить гораздо проще

Почему совместим только с G1?
Нашла только один комментарий по теме: G1 – единственный сборщик мусора, в котором есть pinned regions. Это области, в которых не происходит дефрагментация. Но почему нельзя разместить хэш-таблицу в другом месте — непонятно😕

Почему опция по умолчанию не работает?
Deduplication хорошо экономит память, когда в программе через оператор new создаётся много похожих строк-долгожителей. Тогда экономится больше памяти, чем затраты на копирование в хэш-таблицу. По статистике OpenJDK таких случаев мало, поэтому StringDeduplication по умолчанию отключен.

Для активации добавьте при запуске VM флажок:
-XX:+UseStringDeduplication

Чтобы посмотреть, сколько памяти сэкономил Deduplication, можно вывести статистику Xlog:
-Xlog:stringdedup*=debug

2️⃣ Оптимизация хэш-кода (Java 13)
Хэшкод строки играет важную роль в хэш-таблицах String pool, Deduplication и других процессах JVM.

Внутри класса String хэш хранится как примитив:
int hash

Вычисляется и сохраняется при первом вызове hashcode():
if (hash == 0) {
hash = ...
}

⛔️Минус такого решения:
Если хэш равен 0, то он пересчитывается каждый раз. Шанс этого очень низкий, но в Java 13 в String добавился флаг для нулевого хэша:
boolean hashIsZero

Проверка того, что хэш-код ещё не посчитан, стала такой:
if (h == 0 && !hashIsZero)

Теперь хэш-код всегда считается один раз.

Итого: оптимизацией строк идёт по всем фронтам:
1️⃣ Структура данных (Java 9: Compact Strings)
2️⃣ Кэширование (String pool)
3️⃣ Оптимизация частных случаев (Java 8: String Deduplication)
4️⃣ Микрооптимизации (Java 13: флажок hashIsZero)
👍3
Intellij IDEA: Memory view

Как в IDEA посмотреть количество объектов в памяти?

По умолчанию подсчёт объектов при дебаге выключен, потому что это тяжелая операция и сильно тормозит процесс отладки.

Чтобы включить:
1️⃣ В окне Debug найдите вкладку Memory View.
2️⃣ Нажмите на шестерёнку в Memory View и выберите
Update Loaded Classes On Debugger Stop

Картинка с инструкцией внизу⬇️

Что показывает:
🔸Count - количество экземпляров
🔸Diff - изменения с прошлого шага

При каждой остановке информация о количестве объектов обновляется.

Так можно наглядно посмотреть на работу String pool. Помните, был вопрос:

Сколько строк создаётся в конструкции:
String s = new String("Java");

Откроем Memory View. После выполнения строки напротив класса java.lang.String увидим Diff +2.

Поменяем код на
String java = "Java";
String s = new String("Java");

Запустим снова. "Java" в первой строке отправилась в String pool. Теперь после выполнения второй строки напротив java.lang.String будет Diff +1.
1