Postgres и Kafka, часть 1
Возвращаюсь к ведению канала, и на этой неделе вас ждут 2 огненных поста🔥🔥 о взаимодействии Postgres и Kafka.
Эта пара безумно популярна на большинстве проектов👯♀️ База хранит данные, кафка отвечает за коммуникации между сервисами. Сегодня разберу одну проблему в их отношениях и расскажу вариант решения. А в следующем посте обсудим ещё 2 способа.
Рассмотрим проблему на простом примере.
Пользователь создал заказ, сервис принял запрос. Сервис добавил заказ в базу, и хочет рассказать другим сервисам о новом заказе. Что-то вроде такого:
В чём проблема?
Мы обращаемся к двум отдельным компонентам — базе данных и брокеру сообщений. Каждый из них в любой момент может отвалиться, например, пропадёт связь по сети. В зависимости от порядка строк в saveOrder возможны 2 негативных исхода:
😢 запись в базу сделали, сообщение не отправили
😢 отправили сообщение, но запись в БД не прошла
Получим несоответствие. Поэтому иногда хочется, чтобы события выполнились атомарно: либо оба успешно завершаются, либо ни одно из них.
Большинство разработчиков нетерпеливо скажут: "Что тут думать, нужен transaction outbox!!1". Но если спросить 10 человек, что они под этим понимают, получится 10 разных ответов.
В лучших традициях канала обсудим всё простыми словами:) Очень грубо все решения можно назвать так:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Сегодня рассмотрим первый вариант, в следующем посте — остальные два.
Вариант 1: убираем кафку
У Postgres есть механизм notify/listen, который отправляет уведомления заинтересованным лицам. И вместо отправки сообщений через кафку мы возьмём механизм подписки внутри БД.
База становится единственным компонентом и выполняет оба действия (сохранить в таблицу, уведомить заинтересованных) в одной транзакции.
Чтобы не решать проблемы с координацией двух компонентов, мы переложили всю работу на один.
✅ Образцовая транзакция: атомарность и доставка exactly once
✅ Минимальная задержка между сохранением в базу и уведомлением
❌ Ограниченная функциональность уведомлений
❌ Размытие ответственности — часть уведомлений делает Kafka, часть — Postgres
❌ Увеличение нагрузки на БД
Последний пункт — главный ограничитель, поэтому подход "база делает всё" не очень популярен.
Реализация
Можно взять spring-integration-jdbc и для отправки сообщений, и для получения уведомлений. Документация максимально скудная, дополнительные детали есть в этой статье (под VPN)
В следующем посте обсудим ещё 2 варианта🔥
Возвращаюсь к ведению канала, и на этой неделе вас ждут 2 огненных поста🔥🔥 о взаимодействии Postgres и Kafka.
Эта пара безумно популярна на большинстве проектов👯♀️ База хранит данные, кафка отвечает за коммуникации между сервисами. Сегодня разберу одну проблему в их отношениях и расскажу вариант решения. А в следующем посте обсудим ещё 2 способа.
Рассмотрим проблему на простом примере.
Пользователь создал заказ, сервис принял запрос. Сервис добавил заказ в базу, и хочет рассказать другим сервисам о новом заказе. Что-то вроде такого:
public Order saveOrder(…) {
Order saved = orderRepo.save(…);
kafkaTemplate.send("orders",new OrderCreated(…));
}
Другие сервисы, подписанные на orders, получат сообщение и что-то сделают. Посчитают скидки, обновят статистику, запишут заказ в свою БД и тд.В чём проблема?
Мы обращаемся к двум отдельным компонентам — базе данных и брокеру сообщений. Каждый из них в любой момент может отвалиться, например, пропадёт связь по сети. В зависимости от порядка строк в saveOrder возможны 2 негативных исхода:
😢 запись в базу сделали, сообщение не отправили
😢 отправили сообщение, но запись в БД не прошла
Получим несоответствие. Поэтому иногда хочется, чтобы события выполнились атомарно: либо оба успешно завершаются, либо ни одно из них.
Большинство разработчиков нетерпеливо скажут: "Что тут думать, нужен transaction outbox!!1". Но если спросить 10 человек, что они под этим понимают, получится 10 разных ответов.
В лучших традициях канала обсудим всё простыми словами:) Очень грубо все решения можно назвать так:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Сегодня рассмотрим первый вариант, в следующем посте — остальные два.
Вариант 1: убираем кафку
У Postgres есть механизм notify/listen, который отправляет уведомления заинтересованным лицам. И вместо отправки сообщений через кафку мы возьмём механизм подписки внутри БД.
База становится единственным компонентом и выполняет оба действия (сохранить в таблицу, уведомить заинтересованных) в одной транзакции.
Чтобы не решать проблемы с координацией двух компонентов, мы переложили всю работу на один.
✅ Образцовая транзакция: атомарность и доставка exactly once
✅ Минимальная задержка между сохранением в базу и уведомлением
❌ Ограниченная функциональность уведомлений
❌ Размытие ответственности — часть уведомлений делает Kafka, часть — Postgres
❌ Увеличение нагрузки на БД
Последний пункт — главный ограничитель, поэтому подход "база делает всё" не очень популярен.
Реализация
Можно взять spring-integration-jdbc и для отправки сообщений, и для получения уведомлений. Документация максимально скудная, дополнительные детали есть в этой статье (под VPN)
В следующем посте обсудим ещё 2 варианта🔥
🔥216👍51❤18
Postgres и Kafka, часть 2
В прошлом посте обсуждали, как атомарно записать данные в базу и отправить сообщение в Kafka. Чтобы на случай проблем с сетью компоненты не противоречили друг другу.
Сложность в том, что это БД и месседж брокер — разные сервисы и обеспечить их атомарную работу сложно. Есть 3 подхода к решению проблемы:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Первый пункт уже рассмотрели, в этом посте обсудим варианты 2 и 3.
Вариант 2: убираем БД
При сохранении сущности не делаем сразу запись в базу, только отправляем событие:
В случае проблем с сетью стратегия сервиса такая:
1️⃣ Отправь сообщение, пока не получится
2️⃣ Получив сообщение, пиши в БД, пока не получится
Вместо координации двух компонентов мы добиваемся исполнения гарантий на каждом шаге.
✅ Доступны все возможности Kafka: роутинг, разные гарантии доставки, репликация сообщений
✅ БД не становится узким местом
❌ Самое долгое время между отправкой сообщения и сохранением в БД среди всех вариантов
❌ Возможна неконсистентность данных и нарушение порядка изменений
Другие сервисы могут получить сообщение об изменениях раньше, чем "основной" сервис.
Если развивать вариант "убираем БД" дальше, придём к Event Sourcing. Это подход, в котором основной источник данных — события, а не БД. Но это уже другая история🧚♀️
Вариант 3: добавляем координатор
Оставляем БД исходную задачу по хранению данных, а для отслеживания изменений добавляем отдельный компонент. Общая схема выглядит так:
🔸 В базе данных что-то меняется
🔸 Координатор обнаруживает это изменение
🔸 Транслирует изменение в кафку
База может записывать изменения в отдельную таблицу с помощью триггера, materialized view или логической репликации. Координатор может смотреть на таблицу, а может читать логи БД.
Всё это многообразие объединяется термином Change Data Capture (CDC).
✅ Чёткие зоны ответственности. Postgres хранит данные, Kafka шлёт сообщения, координатор координирует
✅ Огромная гибкость, множество вариантов реализации
❌ Возможна дупликация сообщений
❌ Сложность системы увеличивается
Теперь у нас не 2 компонента (БД и Kafka), а гораздо больше. Больше инстансов, настроек, мониторинга и потраченных человеко-часов.
Реализация
Большинство инструментов базируются на Kafka Connect, в том числе популярная CDC система Debezium. Здесь и остановимся, пока всё просто и понятно:) Оценить, насколько глубока кроличья нора, можно в документации Debezium.
Резюме
Для слаженной работы Postgres и Kafka есть три основных подхода. Они отличаются
✍️ гарантиями доставки сообщения
✍️ нагрузкой на БД
✍️ временем между сохранением в БД и доставкой сообщения
✍️ сложностью
✍️ дополнительными возможностями
Выбираем решение по приоритетам, требованиям и нагрузке и ставим огоньки хорошему посту🔥
В прошлом посте обсуждали, как атомарно записать данные в базу и отправить сообщение в Kafka. Чтобы на случай проблем с сетью компоненты не противоречили друг другу.
Сложность в том, что это БД и месседж брокер — разные сервисы и обеспечить их атомарную работу сложно. Есть 3 подхода к решению проблемы:
1️⃣ Убираем кафку
2️⃣ Убираем БД
3️⃣ Добавляем координатор
Первый пункт уже рассмотрели, в этом посте обсудим варианты 2 и 3.
Вариант 2: убираем БД
При сохранении сущности не делаем сразу запись в базу, только отправляем событие:
public void saveOrder(…) {
kafkaTemplate.send("orders", new OrderCreated(…));
}
Сервис прекращает быть "главным" и становится обычным потребителем событий. Получает сообщение — делает запись в БД. Другие сервисы тоже получают сообщение и что-то делают.В случае проблем с сетью стратегия сервиса такая:
1️⃣ Отправь сообщение, пока не получится
2️⃣ Получив сообщение, пиши в БД, пока не получится
Вместо координации двух компонентов мы добиваемся исполнения гарантий на каждом шаге.
✅ Доступны все возможности Kafka: роутинг, разные гарантии доставки, репликация сообщений
✅ БД не становится узким местом
❌ Самое долгое время между отправкой сообщения и сохранением в БД среди всех вариантов
❌ Возможна неконсистентность данных и нарушение порядка изменений
Другие сервисы могут получить сообщение об изменениях раньше, чем "основной" сервис.
Если развивать вариант "убираем БД" дальше, придём к Event Sourcing. Это подход, в котором основной источник данных — события, а не БД. Но это уже другая история🧚♀️
Вариант 3: добавляем координатор
Оставляем БД исходную задачу по хранению данных, а для отслеживания изменений добавляем отдельный компонент. Общая схема выглядит так:
🔸 В базе данных что-то меняется
🔸 Координатор обнаруживает это изменение
🔸 Транслирует изменение в кафку
База может записывать изменения в отдельную таблицу с помощью триггера, materialized view или логической репликации. Координатор может смотреть на таблицу, а может читать логи БД.
Всё это многообразие объединяется термином Change Data Capture (CDC).
✅ Чёткие зоны ответственности. Postgres хранит данные, Kafka шлёт сообщения, координатор координирует
✅ Огромная гибкость, множество вариантов реализации
❌ Возможна дупликация сообщений
❌ Сложность системы увеличивается
Теперь у нас не 2 компонента (БД и Kafka), а гораздо больше. Больше инстансов, настроек, мониторинга и потраченных человеко-часов.
Реализация
Большинство инструментов базируются на Kafka Connect, в том числе популярная CDC система Debezium. Здесь и остановимся, пока всё просто и понятно:) Оценить, насколько глубока кроличья нора, можно в документации Debezium.
Резюме
Для слаженной работы Postgres и Kafka есть три основных подхода. Они отличаются
✍️ гарантиями доставки сообщения
✍️ нагрузкой на БД
✍️ временем между сохранением в БД и доставкой сообщения
✍️ сложностью
✍️ дополнительными возможностями
Выбираем решение по приоритетам, требованиям и нагрузке и ставим огоньки хорошему посту🔥
🔥268👍18❤13
8 ошибок начинающих разработчиков
Рассмотрим типичную историю начинающего разработчика👶
Чтобы стать программистом, он долго учился. Прошёл много курсов, писал учебные проекты. У них маленькая кодовая база, один пользователь, а главная задача — отработать алгоритм или попробовать фреймворк.
В этих условиях у стажёра сформировался стиль написания кода.
После собеседования стажёр начинает работать в крупном проекте вместе с другими разработчиками. И здесь очень важно перестроиться: многие паттерны, нормальные в учебных проектах, уже не подходят.
В этом посте опишу 8 таких паттернов на простых примерах.
1️⃣ Процедурный стиль
Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:
Во входной параметр
В системах со сложной бизнес-логикой такой подход усложняет чтение, тестирование и возможную параллелизацию.
Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:
2️⃣ Сложные универсальные методы
Задача: по-разному считать скидку для новых пользователей и пользователей с картами лояльности.
Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:
Как лучше: сфокусированные методы для разных ситуаций
3️⃣ Длинные методы
Сложно читать и тестировать, страшно менять.
4️⃣ Любовь к статическим методам
Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше.
5️⃣ Сложное проектирование
Задача: завести три типа пользователей: новые, обычные и VIP. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером.
Как лучше: как можно проще. Например, один класс пользователя с полем Тип. Усложнять при реальной необходимости
PS Все "как лучше" не всегда лучше. Но думаю, идея понятна.
6️⃣ Нулевое или минимальное покрытие тестами
как следствие больших сложных методов и недостаточной инкапсуляции.
7️⃣ Низкий уровень ответственности
Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах:
🔸 Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других:
— Что с задачей, которую я тебе дала 3 дня назад?
— Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё.
🔸 Код не решает проблему, а заметает симптомы:
— Приходил пустой параметр, и я выставил дефолтный. Тесты мешали сделать пул-реквест, и я их отключил.
8️⃣ Слабые коммуникативные навыки
— Как ты починил баг с расчётом ставки?
— Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил
— В чём была ошибка?
— Там два двоеточия вылезло
— Где?
— В дебаге
— 🤯
Эти ошибки ожидаемы в начале работы, я тоже их совершала🙂 Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.
Рассмотрим типичную историю начинающего разработчика👶
Чтобы стать программистом, он долго учился. Прошёл много курсов, писал учебные проекты. У них маленькая кодовая база, один пользователь, а главная задача — отработать алгоритм или попробовать фреймворк.
В этих условиях у стажёра сформировался стиль написания кода.
После собеседования стажёр начинает работать в крупном проекте вместе с другими разработчиками. И здесь очень важно перестроиться: многие паттерны, нормальные в учебных проектах, уже не подходят.
В этом посте опишу 8 таких паттернов на простых примерах.
1️⃣ Процедурный стиль
Задача: получить список заказов пользователя. Новичок скорее всего напишет такой код:
List list = new ArrayList();
process(user, list);
Во входной параметр
list потом запишется результат. Такой стиль часто идёт из учебных заданий по алгоритмам, где экономится каждый бит и максимально сокращается количество объектов.В системах со сложной бизнес-логикой такой подход усложняет чтение, тестирование и возможную параллелизацию.
Как лучше: использовать выходные параметры, не менять входные данные, давать осмысленные имена методам:
List orders = getOrders(user);
2️⃣ Сложные универсальные методы
Задача: по-разному считать скидку для новых пользователей и пользователей с картами лояльности.
Новички часто используют принцип Don't Repeat Yourself на максималках и пишут универсальный метод с кучей параметров и десятком if внутри:
getDiscount(user, true, true, limit, true)
Как лучше: сфокусированные методы для разных ситуаций
getDiscountNew(user);
getDiscountLoyal(user, limit)
3️⃣ Длинные методы
Сложно читать и тестировать, страшно менять.
4️⃣ Любовь к статическим методам
Как лучше: небольшие методы, связанные с конкретным классом. Связность ниже, ошибок меньше.
5️⃣ Сложное проектирование
Задача: завести три типа пользователей: новые, обычные и VIP. Новички скорее всего сделают интерфейс, 3 класса и статический класс с фабричными методами и билдером.
Как лучше: как можно проще. Например, один класс пользователя с полем Тип. Усложнять при реальной необходимости
PS Все "как лучше" не всегда лучше. Но думаю, идея понятна.
6️⃣ Нулевое или минимальное покрытие тестами
как следствие больших сложных методов и недостаточной инкапсуляции.
7️⃣ Низкий уровень ответственности
Пункт не относится к разработке, но очень актуален для начинающих. Проявляется в двух формах:
🔸 Непонятно, что происходит. Человек сидит и молчит до последнего, пока не спросишь статус задачи или про возможные трудности. Он умалчивает проблемы или переносит на других:
— Что с задачей, которую я тебе дала 3 дня назад?
— Я не понял, куда смотреть, потом меня HR позвал бумаги подписывать, потом я настраивал гит, увидел другую задачу и переключился на неё.
🔸 Код не решает проблему, а заметает симптомы:
— Приходил пустой параметр, и я выставил дефолтный. Тесты мешали сделать пул-реквест, и я их отключил.
8️⃣ Слабые коммуникативные навыки
— Как ты починил баг с расчётом ставки?
— Там через геттер фабричный метод нашёл, и потом докер с постгрёй поднял посмотреть, в логах был фильтр урезанный, я письмо отправил тебе в цц, но вроде скоуп не тот или тот, короче, запушил
— В чём была ошибка?
— Там два двоеточия вылезло
— Где?
— В дебаге
— 🤯
Эти ошибки ожидаемы в начале работы, я тоже их совершала🙂 Чем быстрее вы перестроитесь на командный стиль разработки, тем вероятнее пройдёте испытательный срок и быстрее вольётесь в проект.
🔥122👍73❤28👎11
С новым годом!
Любимые подписчики, пусть в следующем году выгорят только бенгальские огни, а работа принесёт море удовольствия и денег. Пусть жизнь за пределами работы вас вдохновляет и наполняет❤️
Я к концу года очень устала, и на канал не осталось сил. Но отдых творит чудеса, поэтому скоро вернусь в строй. Вы тоже отдохните как следует, вы это заслужили!
С наступающим новым годом!🎄
Любимые подписчики, пусть в следующем году выгорят только бенгальские огни, а работа принесёт море удовольствия и денег. Пусть жизнь за пределами работы вас вдохновляет и наполняет❤️
Я к концу года очень устала, и на канал не осталось сил. Но отдых творит чудеса, поэтому скоро вернусь в строй. Вы тоже отдохните как следует, вы это заслужили!
С наступающим новым годом!🎄
🎄256❤107🔥32👍7
Как вредит ChatGPT
ChatGPT всё глубже проникает в жизнь, но не всегда улучшает её в лучшую сторону. За последние полгода появились две неприятные тендеции, которые больно бьют по начинающим разработчикам.
Нейросети — прекрасный инструмент, и в работе с ним есть два важных этапа:
👆 задать вопрос
✌️ провалидировать ответ
Второй пункт очень важен. ЧатЖПТ и другие нейросетки — не гигантская сеть знаний. Это искусственная модель, которая пытается угадать ответ, а задача пользователя — провалидировать его. Но бывает, что этап валидации пропускается, и мы получаем
Следствие 1: низкий уровень знаний после курсов
Иногда ученики курсов ленятся разбираться в непонятном. Тогда они спрашивают ответ у нейросети и сдают его на проверку.
Пользы от такого "обучения" нет. Лучше ошибиться и разобрать ошибку с преподавателем, чем получить формальный зачёт и забить не возможный пробел.
На курсе многопоточки я не принимала ответы нейросети и подробно разбирала затык ученика. Но не все преподаватели так же заботятся об усвоении знаний, ведь с виду ответы выглядят отлично.
Так что скоро увидим больше выпускников курсов с формальным дипломом и поверхностными знаниями. Ситуация на рынке джунов и так печальная, а станет ещё хуже😔
✍️ Что делать:
Не используйте нейросети для обучения. Если что-то непонятно — спросите преподавателя, поищите статьи на хабре/baeldung, почитайте документацию.
Особенно это касается практики. Во многих вещах надо "набить руку", и в процессе работы всплывают все непонятные моменты. А готовый ответ выветрится из головы через секунду.
Следствие 2: низкое качество контента в "Java" каналах
Вы точно видели такие каналы — посты с рандомными фактами и обильная реклама курсов. Раньше в основе контента был копипаст из устаревших статей, переводы и куски из документации. Не очень интересно, но кому-то норм.
Сейчас чаще появляются посты, сделанные при участии ChatGPT. Качество контента не выросло, но теперь в постах проскальзывают ошибки.
Парочка примеров:
🤔 Пост о StringBuffer и противоречивые абзацы насчёт потокобезопасности:
Класс является потокобезопасным, т. е. может использоваться в многопоточных приложениях.
Методы StringBuffer не синхронизированы, поэтому для многопоточного доступа нужно вручную синхронизировать доступ с помощью synchronized блока.
🤔 Пост о методе compareAndSwap():
Такого метода в атомиках вообще нет. В описании снова находим противоречие — метод возвращает то ли результат операции, то ли текущее значение переменной.
Хорошо, если в комментариях найдётся молодец, который укажет на ошибку. Но комментарии читают не всегда, а админам безразлично, что публиковать. Создавать контент с ChatGPT быстро и дешево, в будущем его будет больше.
При использовании нейросети человек отдаёт себе отчёт, что это нейросеть, и может засомневаться в ответе. А посты в "Java" канале реже подвергаются сомнению.
✍️ Что делать:
Отписаться от таких каналов. В интернете петабайты материалов для новичков, в которых точно нет ошибок. Даже устаревший текст лучше неверной информации.
И конечно, читать каналы разработчиков, которые пишут авторский контент и делятся опытом. Люди тоже ошибаются, но качество таких постов гораздо выше. Не забывайте про лайки и обратную связь, это правда очень мотивирует писать дальше🥰
ChatGPT всё глубже проникает в жизнь, но не всегда улучшает её в лучшую сторону. За последние полгода появились две неприятные тендеции, которые больно бьют по начинающим разработчикам.
Нейросети — прекрасный инструмент, и в работе с ним есть два важных этапа:
👆 задать вопрос
✌️ провалидировать ответ
Второй пункт очень важен. ЧатЖПТ и другие нейросетки — не гигантская сеть знаний. Это искусственная модель, которая пытается угадать ответ, а задача пользователя — провалидировать его. Но бывает, что этап валидации пропускается, и мы получаем
Следствие 1: низкий уровень знаний после курсов
Иногда ученики курсов ленятся разбираться в непонятном. Тогда они спрашивают ответ у нейросети и сдают его на проверку.
Пользы от такого "обучения" нет. Лучше ошибиться и разобрать ошибку с преподавателем, чем получить формальный зачёт и забить не возможный пробел.
На курсе многопоточки я не принимала ответы нейросети и подробно разбирала затык ученика. Но не все преподаватели так же заботятся об усвоении знаний, ведь с виду ответы выглядят отлично.
Так что скоро увидим больше выпускников курсов с формальным дипломом и поверхностными знаниями. Ситуация на рынке джунов и так печальная, а станет ещё хуже😔
✍️ Что делать:
Не используйте нейросети для обучения. Если что-то непонятно — спросите преподавателя, поищите статьи на хабре/baeldung, почитайте документацию.
Особенно это касается практики. Во многих вещах надо "набить руку", и в процессе работы всплывают все непонятные моменты. А готовый ответ выветрится из головы через секунду.
Следствие 2: низкое качество контента в "Java" каналах
Вы точно видели такие каналы — посты с рандомными фактами и обильная реклама курсов. Раньше в основе контента был копипаст из устаревших статей, переводы и куски из документации. Не очень интересно, но кому-то норм.
Сейчас чаще появляются посты, сделанные при участии ChatGPT. Качество контента не выросло, но теперь в постах проскальзывают ошибки.
Парочка примеров:
🤔 Пост о StringBuffer и противоречивые абзацы насчёт потокобезопасности:
Класс является потокобезопасным, т. е. может использоваться в многопоточных приложениях.
Методы StringBuffer не синхронизированы, поэтому для многопоточного доступа нужно вручную синхронизировать доступ с помощью synchronized блока.
🤔 Пост о методе compareAndSwap():
Такого метода в атомиках вообще нет. В описании снова находим противоречие — метод возвращает то ли результат операции, то ли текущее значение переменной.
Хорошо, если в комментариях найдётся молодец, который укажет на ошибку. Но комментарии читают не всегда, а админам безразлично, что публиковать. Создавать контент с ChatGPT быстро и дешево, в будущем его будет больше.
При использовании нейросети человек отдаёт себе отчёт, что это нейросеть, и может засомневаться в ответе. А посты в "Java" канале реже подвергаются сомнению.
✍️ Что делать:
Отписаться от таких каналов. В интернете петабайты материалов для новичков, в которых точно нет ошибок. Даже устаревший текст лучше неверной информации.
И конечно, читать каналы разработчиков, которые пишут авторский контент и делятся опытом. Люди тоже ошибаются, но качество таких постов гораздо выше. Не забывайте про лайки и обратную связь, это правда очень мотивирует писать дальше🥰
👍382🔥99❤78👎5
От JDBC до Spring Data: часть 1
Общение с базой данных связано с огромным количеством технологий — навскидку вспоминаем JDBC, JPA, Hibernate и Spring Data. Они тесно переплетены, и не всегда люди чётко понимают, что есть что и зачем нужно.
В ближайших постах разложу по полочкам основные технологии по работе с данными.
❓Почему всё так сложно? Почему нельзя сохранить всё как есть?
Приложение использует оперативную память и представляет данные в виде объектов. Можно работать с любым объектом в любой момент — чтение и запись происходят быстро. Минус оперативки — когда приложение завершается, память освобождается, и данные пропадают.
Чтобы данные пережили приложение, они записываются в постоянную память. Хранением и организацией данных занимается БД.
В оперативке данные лежат кое-как — где место нашлось, там объект и создаётся. В постоянной памяти данных много, всё упорядочено и оптимизировано. Поэтому модели данных в БД и приложении иногда отличаются.
А ещё БД — это отдельное приложение. В итоге для сохранения/извлечения данных нужна куча дополнительной работы:
🔸 открыть соединение
🔸 составить SQL запрос
🔸 передать в запросе данные из приложения
🔸 преобразовать ответ БД в java объект
🔸 закрыть соединение
JDBC
— стандартные java методы, которые выполняют все пункты выше. Все инструменты по работе с БД под капотом используют как раз JDBC.
Работа с соединениями и преобразование данных — утомительная работа. Поэтому и появляются библиотеки, облегчающие этот труд.
Spring JDBC
берёт на себя работу с соединениями. Разработчик всё так же составляет запросы, передаёт параметры и преобразует ответы в java объекты.
ORM
Object Relational Mapping — преобразование данных (mapping) из java объектов в сущности БД и обратно.
Формально, ORM — просто название процесса. В случае JDBC весь ОRМ разработчик делает вручную.
На практике под ORM подразумевают ORM библиотеку/фреймворк — какой-нибудь инструмент, который берёт на себя часть работы по запросам и преобразованию данных.
Hibernate
— самая популярная ORM библиотека. Составляет простые SQL запросы и преобразует данные. Упростил жизнь многим и заслужил их любовь❤️
В хибернейте не всё идеально:
▪️ Работа с соединениями (сессиями) остаётся на пользователе
▪️ Для корректной работы надо знать внутрянку (dirty session, как разруливаются отношения и тд).
Сложно не признать, что Hibernate великолепен. Ворвался на олимп ORM библиотек в 2001 году и до сих пор оттуда не слезает🏆
JPA
Сейчас большинство приложений базируются на спринге, но 10-15 лет назад приложения часто опирались на Java ЕЕ. В те года ORM Java ЕЕ выглядел сложно — для каждой сущности требовались несколько классов и кучка интерфейсов.
Hibernate выглядел привлекательно, но нельзя просто взять и добавить библиотеку в проект. Во вселенной Java EE всё работает через спецификации — стандартные интерфейсы.
Поэтому появилась новая спека по ORM — Java Persistence API или JPA. С небольшими отличиями почти полностью списана с хибернейта. Вскоре Hibernate подстроился под JPA и стал использоваться в Java EE.
Итого
⭐️ JDBC — базовое API по работе с БД
⭐️ ORM — преобразование данных между приложением и БД. На практике под “у нас на проекте ORM” имеют в виду, что используется ORM библиотека, например, Hibernate
⭐️ JPA — спецификация по ORM. Набор интерфейсов, аннотаций и описание, как всё должно работать. Не включает в себя конкретную реализацию
⭐️ Hibernate — популярная ORM библиотека, реализующая JPA
В следующем посте распишу вариации Spring Data, и почему материалы по хибернейт могут не соответствовать реальности.
Общение с базой данных связано с огромным количеством технологий — навскидку вспоминаем JDBC, JPA, Hibernate и Spring Data. Они тесно переплетены, и не всегда люди чётко понимают, что есть что и зачем нужно.
В ближайших постах разложу по полочкам основные технологии по работе с данными.
❓Почему всё так сложно? Почему нельзя сохранить всё как есть?
Приложение использует оперативную память и представляет данные в виде объектов. Можно работать с любым объектом в любой момент — чтение и запись происходят быстро. Минус оперативки — когда приложение завершается, память освобождается, и данные пропадают.
Чтобы данные пережили приложение, они записываются в постоянную память. Хранением и организацией данных занимается БД.
В оперативке данные лежат кое-как — где место нашлось, там объект и создаётся. В постоянной памяти данных много, всё упорядочено и оптимизировано. Поэтому модели данных в БД и приложении иногда отличаются.
А ещё БД — это отдельное приложение. В итоге для сохранения/извлечения данных нужна куча дополнительной работы:
🔸 открыть соединение
🔸 составить SQL запрос
🔸 передать в запросе данные из приложения
🔸 преобразовать ответ БД в java объект
🔸 закрыть соединение
JDBC
— стандартные java методы, которые выполняют все пункты выше. Все инструменты по работе с БД под капотом используют как раз JDBC.
Работа с соединениями и преобразование данных — утомительная работа. Поэтому и появляются библиотеки, облегчающие этот труд.
Spring JDBC
берёт на себя работу с соединениями. Разработчик всё так же составляет запросы, передаёт параметры и преобразует ответы в java объекты.
ORM
Object Relational Mapping — преобразование данных (mapping) из java объектов в сущности БД и обратно.
Формально, ORM — просто название процесса. В случае JDBC весь ОRМ разработчик делает вручную.
На практике под ORM подразумевают ORM библиотеку/фреймворк — какой-нибудь инструмент, который берёт на себя часть работы по запросам и преобразованию данных.
Hibernate
— самая популярная ORM библиотека. Составляет простые SQL запросы и преобразует данные. Упростил жизнь многим и заслужил их любовь❤️
В хибернейте не всё идеально:
▪️ Работа с соединениями (сессиями) остаётся на пользователе
▪️ Для корректной работы надо знать внутрянку (dirty session, как разруливаются отношения и тд).
Сложно не признать, что Hibernate великолепен. Ворвался на олимп ORM библиотек в 2001 году и до сих пор оттуда не слезает🏆
JPA
Сейчас большинство приложений базируются на спринге, но 10-15 лет назад приложения часто опирались на Java ЕЕ. В те года ORM Java ЕЕ выглядел сложно — для каждой сущности требовались несколько классов и кучка интерфейсов.
Hibernate выглядел привлекательно, но нельзя просто взять и добавить библиотеку в проект. Во вселенной Java EE всё работает через спецификации — стандартные интерфейсы.
Поэтому появилась новая спека по ORM — Java Persistence API или JPA. С небольшими отличиями почти полностью списана с хибернейта. Вскоре Hibernate подстроился под JPA и стал использоваться в Java EE.
Итого
⭐️ JDBC — базовое API по работе с БД
⭐️ ORM — преобразование данных между приложением и БД. На практике под “у нас на проекте ORM” имеют в виду, что используется ORM библиотека, например, Hibernate
⭐️ JPA — спецификация по ORM. Набор интерфейсов, аннотаций и описание, как всё должно работать. Не включает в себя конкретную реализацию
⭐️ Hibernate — популярная ORM библиотека, реализующая JPA
В следующем посте распишу вариации Spring Data, и почему материалы по хибернейт могут не соответствовать реальности.
🔥193👍76❤25👎12
От JDBC до Spring Data: часть 2
В этом посте расскажу про популярные модули Spring Data и подскажу важный нюанс при изучении Hibernate.
Spring Data JPA
Hibernate вызвал вау-эффект тем, что взял на себя маппинг и простейшие SQL запросы.
Spring Data JPA пошёл дальше и избавил разработчика от унылых конфигов и возни с сессиями, плюс генерирует более сложные SQL запросы.
Всё очень круто (без шуток), но есть нюанс.
Ванильный Hibernate подразумевает, что пользователь знаком с деталями реализации — умело работает с кэшами 1 и 2 уровня, различает
А вот JPA определяет только интерфейс доступа к данным. Поэтому в Spring Data JPA многие хибернейт фичи не используются.
Пример — ленивая загрузка коллекций и кэш 1 уровня. Spring Data в общем случае при каждом обращении к репозиторию создаёт новую сессию. Кэширования в итоге нет, а при загрузке коллекций ловим эксепшн.
Кэш 2 уровня и EntityGraph поправят ситуацию, но это уже продвинутый уровень:) Недостаточно пользоваться абстракцией "репозиторий", надо знать и Hibernate, и как Spring использует Hibernate.
Практический совет — если что-то читаете по хибернейту, уточняйте, как это работает в Spring Data и работает ли вообще.
Для простых сервисов Spring Data JPA существенно упрощает жизнь. Для сложных тоже, но требует больше знаний.
Spring Data JDBC
— альтернатива Spring Data JPA. Под капотом у него JDBC без посредничества Hibernate.
Интерфейс такой же — пользователь работает с репозиторием и размечает классы аннотациями типа @Id или @Column.
JDBC проще, у него нет кэшей, ленивой загрузки, каскадных операций и автоматического сохранения. Код становится предсказуемым, но многие вещи нужно делать явно.
Отдельного внимания заслуживает работа с зависимыми сущностями в DDD стиле. А в этом докладе показан наглядный пример и больше различий Spring Data JPA/JDBC.
Важный момент! Не путайте две библиотеки:
🌸 Spring JDBC упрощает работу с соединениями. Запросы, маппинг сущностей, управление транзакциями пишет разработчик
🌹 Spring Data JDBC даёт следующий уровень абстрации — репозиторий. Работа c запросами, маппингом и транзациями упрощается за счёт аннотаций
MyBatis
часто упоминается как альтернатива Hibernate. Называет себя persistence framework, а не ORM, но занимается тем же — помогает писать меньше кода по перегону данных между БД и приложением.
Основное отличие MyBatis от хибернейта — все SQL-запросы пишутся явно, и внутри можно писать if и foreach блоки.
MyBatis в целом ничего, но редко встречается. Причины просты:
❌ Нет Spring Data модуля, только Spring Boot Starter. Писать руками нужно гораздо больше
❌ В MyBatis есть аннотации, но документация и большинство статей используют XML. Выглядит несовременно👨🦳
Итого
⭐️ Spring Data * берёт на себя конфиги, работу с сессиями, генерацию некоторых запросов
⭐️ Spring Data JPA упрощает работу с Hibernate
⭐️ Spring Data JDBC предлагает похожий интерфейс, но на основе JDBC
⭐️ MyBatis для тех, кто хочет чего-то другого
Что выбрать?
Функционально Spring Data JPA/JDBC и MyBatis похожи, но со своими нюансами. Адекватных и современных бенчмарков в интернете нет. Статьи вроде "Hibernate vs MyBatis" очень поверхностные, не тратьте на них время.
На практике выбор делается почти случайно. Что затащат в проект на старте, то и используется:)
В этом посте расскажу про популярные модули Spring Data и подскажу важный нюанс при изучении Hibernate.
Spring Data JPA
Hibernate вызвал вау-эффект тем, что взял на себя маппинг и простейшие SQL запросы.
Spring Data JPA пошёл дальше и избавил разработчика от унылых конфигов и возни с сессиями, плюс генерирует более сложные SQL запросы.
Всё очень круто (без шуток), но есть нюанс.
Ванильный Hibernate подразумевает, что пользователь знаком с деталями реализации — умело работает с кэшами 1 и 2 уровня, различает
persist/save/merge, использует нужные типы данных и тд.А вот JPA определяет только интерфейс доступа к данным. Поэтому в Spring Data JPA многие хибернейт фичи не используются.
Пример — ленивая загрузка коллекций и кэш 1 уровня. Spring Data в общем случае при каждом обращении к репозиторию создаёт новую сессию. Кэширования в итоге нет, а при загрузке коллекций ловим эксепшн.
Кэш 2 уровня и EntityGraph поправят ситуацию, но это уже продвинутый уровень:) Недостаточно пользоваться абстракцией "репозиторий", надо знать и Hibernate, и как Spring использует Hibernate.
Практический совет — если что-то читаете по хибернейту, уточняйте, как это работает в Spring Data и работает ли вообще.
Для простых сервисов Spring Data JPA существенно упрощает жизнь. Для сложных тоже, но требует больше знаний.
Spring Data JDBC
— альтернатива Spring Data JPA. Под капотом у него JDBC без посредничества Hibernate.
Интерфейс такой же — пользователь работает с репозиторием и размечает классы аннотациями типа @Id или @Column.
JDBC проще, у него нет кэшей, ленивой загрузки, каскадных операций и автоматического сохранения. Код становится предсказуемым, но многие вещи нужно делать явно.
Отдельного внимания заслуживает работа с зависимыми сущностями в DDD стиле. А в этом докладе показан наглядный пример и больше различий Spring Data JPA/JDBC.
Важный момент! Не путайте две библиотеки:
🌸 Spring JDBC упрощает работу с соединениями. Запросы, маппинг сущностей, управление транзакциями пишет разработчик
🌹 Spring Data JDBC даёт следующий уровень абстрации — репозиторий. Работа c запросами, маппингом и транзациями упрощается за счёт аннотаций
MyBatis
часто упоминается как альтернатива Hibernate. Называет себя persistence framework, а не ORM, но занимается тем же — помогает писать меньше кода по перегону данных между БД и приложением.
Основное отличие MyBatis от хибернейта — все SQL-запросы пишутся явно, и внутри можно писать if и foreach блоки.
MyBatis в целом ничего, но редко встречается. Причины просты:
❌ Нет Spring Data модуля, только Spring Boot Starter. Писать руками нужно гораздо больше
❌ В MyBatis есть аннотации, но документация и большинство статей используют XML. Выглядит несовременно👨🦳
Итого
⭐️ Spring Data * берёт на себя конфиги, работу с сессиями, генерацию некоторых запросов
⭐️ Spring Data JPA упрощает работу с Hibernate
⭐️ Spring Data JDBC предлагает похожий интерфейс, но на основе JDBC
⭐️ MyBatis для тех, кто хочет чего-то другого
Что выбрать?
Функционально Spring Data JPA/JDBC и MyBatis похожи, но со своими нюансами. Адекватных и современных бенчмарков в интернете нет. Статьи вроде "Hibernate vs MyBatis" очень поверхностные, не тратьте на них время.
На практике выбор делается почти случайно. Что затащат в проект на старте, то и используется:)
🔥124👍70❤23👎6
Выполнится ли до конца задача, отправленная в экзекьютор? Длительность задачи - около 500мс
Anonymous Poll
25%
Да, если она начала выполнение до вызова cancel
16%
Нет
59%
Невозможно определить
👍12🔥7
Best practice: не использовать boolean параметры в методах
На вопрос выше сходу ответят только знатоки многопоточки. Остальные наверняка попытались придумать, что означает
Метод
Public методы определяют интерфейс. В хорошем API пользователю не нужно зарываться в доки, чтобы понять, как пользоваться классом.
Даже если у параметров хорошие имена, итоговые true и false выглядят несимпатично и неудобно.
С private методами ситуация другая. Внутренняя реализация должна быть читаемой и понятной, но требования всё же менее жёсткие, чем у public методов. В private методах флажки допустимы, но увлекаться не стоит:)
Как исправить метод с boolean параметром?
1️⃣ Сделать два метода
и зашить "особенность" флажка в название. Необязательно дублировать реализацию — public методы могут под капотом вызывать private метод с флажком.
Так сделано, например, в классе
2️⃣ Создать enum с двумя значениями и передавать его вместо флажка
Такой вариант предлагается в книге Effective Java (Item 51).
Выглядит лучше, чем флажки, но на практике такой способ встречается редко. Если enum нужен только для обозначения true/false, быстрее и проще сделать 2 метода с разными именами.
🔥 Ответ на вопрос перед постом — "невозможно определить".
Мы не знаем точно, начала ли задача выполнение до вызова cancel. Даже если задача стартовала, успех cancel зависит от кода внутри задачи. Поэтому итог непредсказуем🤷♀️
На вопрос выше сходу ответят только знатоки многопоточки. Остальные наверняка попытались придумать, что означает
cancel(true), и в чём разница с cancel(false).Метод
Future#cancel — наглядная иллюстрация, почему boolean параметры в public методах не ок. Потому что непонятно, что означают true и false.Public методы определяют интерфейс. В хорошем API пользователю не нужно зарываться в доки, чтобы понять, как пользоваться классом.
Даже если у параметров хорошие имена, итоговые true и false выглядят несимпатично и неудобно.
С private методами ситуация другая. Внутренняя реализация должна быть читаемой и понятной, но требования всё же менее жёсткие, чем у public методов. В private методах флажки допустимы, но увлекаться не стоит:)
Как исправить метод с boolean параметром?
1️⃣ Сделать два метода
и зашить "особенность" флажка в название. Необязательно дублировать реализацию — public методы могут под капотом вызывать private метод с флажком.
Так сделано, например, в классе
HashMap для методов put(k,v) и putIfAbsent(k,v). Оба в итоге вызывают private метод putVal с флажком ifAbsent2️⃣ Создать enum с двумя значениями и передавать его вместо флажка
enum ReplicateStatus {NO_REPLICATION, REPLICATE};
...
saveRequest(req, ReplicateStatus.REPLICATE);Такой вариант предлагается в книге Effective Java (Item 51).
Выглядит лучше, чем флажки, но на практике такой способ встречается редко. Если enum нужен только для обозначения true/false, быстрее и проще сделать 2 метода с разными именами.
🔥 Ответ на вопрос перед постом — "невозможно определить".
Мы не знаем точно, начала ли задача выполнение до вызова cancel. Даже если задача стартовала, успех cancel зависит от кода внутри задачи. Поэтому итог непредсказуем🤷♀️
👍118🔥60❤27👎3
Java 22: изменения в Stream API (с плохим API)
19 марта выйдет Java 22. Там появится интересная превью фича Stream Gatherers. Коротко расскажу, что это, зачем нужно, и что с ней не так.
Начнём. Код со Stream API делится на 3 части:
▫️ источник данных
▫️ промежуточные операции: map, filter, flatMap
▫️ терминальная операция: collect, count, findAny
Промежуточных операций немного, но они легко покрывают большую часть задач.
Иногда их недостаточно. Например, хочется сделать distinct по какому-то признаку и дальше работать с исходными объектами:
Но это невозможно, distinct работает только по equals. В итоге надо либо извращаться, либо переписывать на for.
В Java 22 в Stream API появится универсальный метод gather. Туда можно передать логику преобразований с любыми отношениями - 1:1, 1:N, N:1, N:N. Синтаксис сложный, но круто, что такая возможность появилась.
Второе нововведение. Для collect есть готовые статические методы в классе Collectors, а для gather появится Gatherers с новыми методами. Самое полезное — два метода по работе с окнами:
🪟 windowFixed — поделить стрим на подмножества заданного размера
🪟 windowSliding — подмножества с пересечением одного элемента
Что не так c методом gather?
1️⃣ Многословность
Та же ситуация, что и с collect. Чтобы разбить список на окна, придётся написать
Даже со статическим импортом выглядит не очень. Хочется писать без лишних слов:
Да, исходный код Stream API сложный, и его тяжело расширять. Да, через статические методы реализация получится проще. Но не вижу ничего невозможного, чтобы сделать новые методы лаконичными, без приставки "
2️⃣ Странные новые методы
Начнём с оконных. windowSliding делает пересечение только по одному элементу. Зачем нужно это ограничение — непонятно. Так же непонятно, зачем делать два отдельных метода windowFixed и windowSliding.
За образец можно взять Kotlin:
Первый параметр задаёт размер окна, второй — шаг, с которым идём по списку. Удобно и понятно.
Ещё в Gatherers появятся три странных метода: fold, mapConcurrent и scan. С первого взгляда непонятно, зачем они нужны, очень уж специфичны.
В целом криминала в gather/Gatherers нет, жить можно. Но важный навык разработчика — замечать слабые места в своих и чужих решениях. Этот навык нужно развивать, для этого и нужен этот пост:)
Что ещё почитать:
🔥 Серия постов про коллекторы. Там же я рассказала, почему в стримах используется отдельный метод collect, а не просто toList()
🔥 Новые методы Stream API в Java 16
🔥 Критикую метод HashMap в Java 20. Хотя сам метод маленький, он показывает серьёзную ошибку проектирования API
19 марта выйдет Java 22. Там появится интересная превью фича Stream Gatherers. Коротко расскажу, что это, зачем нужно, и что с ней не так.
Начнём. Код со Stream API делится на 3 части:
▫️ источник данных
▫️ промежуточные операции: map, filter, flatMap
▫️ терминальная операция: collect, count, findAny
Промежуточных операций немного, но они легко покрывают большую часть задач.
Иногда их недостаточно. Например, хочется сделать distinct по какому-то признаку и дальше работать с исходными объектами:
clients.stream()
.distinct(Client::getBonusType)
.map(Client::getId).forEach(…)
Но это невозможно, distinct работает только по equals. В итоге надо либо извращаться, либо переписывать на for.
В Java 22 в Stream API появится универсальный метод gather. Туда можно передать логику преобразований с любыми отношениями - 1:1, 1:N, N:1, N:N. Синтаксис сложный, но круто, что такая возможность появилась.
Второе нововведение. Для collect есть готовые статические методы в классе Collectors, а для gather появится Gatherers с новыми методами. Самое полезное — два метода по работе с окнами:
🪟 windowFixed — поделить стрим на подмножества заданного размера
[1,2,3,4,5,6] → windowFixed(3) → [1,2,3], [4,5,6]
🪟 windowSliding — подмножества с пересечением одного элемента
[1,2,3,4,5,6,7] → windowSliding(3) → [1,2,3], [3,4,5], [5,6,7]
Что не так c методом gather?
1️⃣ Многословность
Та же ситуация, что и с collect. Чтобы разбить список на окна, придётся написать
list.stream()
.gather(Gatherers.windowFixed(2))
.collect(Collectors.toList())
Даже со статическим импортом выглядит не очень. Хочется писать без лишних слов:
list.stream()
.windowFixed(2)
.toList()
Да, исходный код Stream API сложный, и его тяжело расширять. Да, через статические методы реализация получится проще. Но не вижу ничего невозможного, чтобы сделать новые методы лаконичными, без приставки "
gather(Gatherers"2️⃣ Странные новые методы
Начнём с оконных. windowSliding делает пересечение только по одному элементу. Зачем нужно это ограничение — непонятно. Так же непонятно, зачем делать два отдельных метода windowFixed и windowSliding.
За образец можно взять Kotlin:
list.windowed(5,2)
Первый параметр задаёт размер окна, второй — шаг, с которым идём по списку. Удобно и понятно.
Ещё в Gatherers появятся три странных метода: fold, mapConcurrent и scan. С первого взгляда непонятно, зачем они нужны, очень уж специфичны.
В целом криминала в gather/Gatherers нет, жить можно. Но важный навык разработчика — замечать слабые места в своих и чужих решениях. Этот навык нужно развивать, для этого и нужен этот пост:)
Что ещё почитать:
🔥 Серия постов про коллекторы. Там же я рассказала, почему в стримах используется отдельный метод collect, а не просто toList()
🔥 Новые методы Stream API в Java 16
🔥 Критикую метод HashMap в Java 20. Хотя сам метод маленький, он показывает серьёзную ошибку проектирования API
🔥126👍69❤23👎7
Чего ждать от Java ближайшие 10 лет?
Новые фичи в Java не создаются в вакууме, а объединяются в группы с конкретной целью. Каждый релиз — небольшие шаги в сторону этой цели. Расскажу про основные текущие проекты.
⭐️ Project Loom
Цель: добавить легковесные(виртуальные) потоки
Самая заметная фича со времён Stream API. Большинство проектов получат огромный буст от внедрения виртуальных потоков. Что важно — с минимальными изменениями в коде.
В Java 21 вышло базовое апи по работе с виртуальными потоками. Предстоит ещё много работы внутри JVM и в рамках языка, чтобы удобно управлять тысячами задач.
⭐️ ZGC / Shenandoah
Цель: сборщик мусора с минимальными паузами
Сборщики чуть отличаются по реализации, но задача одна — обеспечить минимум простоя во время сборки мусора. Разумеется, не бесплатно. Паузы уменьшаются, но увеличивается расход памяти и снижается пропускная способность.
Для большинства проектов это не актуально. Сборщик по умолчанию G1 отлично работает и становится лучше с каждым релизом.
⭐️ Project Panama
Цель: упростить работу с native кодом и памятью за пределами JVM
Проект делится на 2 направления:
🔹 Новый вариант JNI
Нужен для работы с библиотеками, которые не написаны на Java, и вряд ли когда-нибудь будут: работа с графикой, манипуляции с ОС, сетью и тд. Текущий JNI очень старый, работает медленно и не безопасен. Поэтому пишут новый:)
🔹 Работа с памятью за пределами JVM
Нужна проектам, которые хотят управлять памятью без посредничества Java. Самим делать сборку мусора, сжимать и раскладывать данные по определённым структурам.
⭐️ Project Amber
Цель: упростить язык, добавить новые конструкции
Самые "народные" фичи, которые часто попадают в обзоры и статьи:
Что-то получается хорошо, что-то не очень. Где-то много пафосных разговоров про data-oriented programming. Есть странные фичи, вроде упрощения написания Hello world.
⭐️ Project Leyden
Цель: ускорить время старта Java программ
Оптимизировать загрузку классов, линковку, перенести часть процессов на этап компиляции. На энтерпрайз повлияет мало, по сравнению с работой фреймворков ускорения на уровне JVM будут мало заметны.
⭐️ Project Valhalla
Цель: оптимизировать работу с данными
Здесь так же два направления:
🔹 Создать value types — объект с полями и методами, работа с которым идёт как с примитивом:
✅ Передаётся по значению
✅ Компактно лежит в памяти
✅ Не может быть null
🔹 Создать общую схему работы с примитивами, объектами и value types, избавить разработчика от мыслей про boxing/unboxing
❓ А когда будет готово?
Плохая новость — реализации всех проектов растягиваются на десятки лет.
10 лет — не преувеличение. Лямбда-выражения в java обсуждались с 2004 года, а увидели свет только в 2014.
В случае Java медлительность — это фича и часть стратегии: смотреть, как решаются проблемы в других языках и не изобретать велосипед. Осторожно выбирать, что войдёт в язык, а что — нет, тщательно продумывать архитектуру.
На java пишут большие системы, которые работают десятки лет. Поэтому основательный подход абсолютно оправдан😌
Новые фичи в Java не создаются в вакууме, а объединяются в группы с конкретной целью. Каждый релиз — небольшие шаги в сторону этой цели. Расскажу про основные текущие проекты.
⭐️ Project Loom
Цель: добавить легковесные(виртуальные) потоки
Самая заметная фича со времён Stream API. Большинство проектов получат огромный буст от внедрения виртуальных потоков. Что важно — с минимальными изменениями в коде.
В Java 21 вышло базовое апи по работе с виртуальными потоками. Предстоит ещё много работы внутри JVM и в рамках языка, чтобы удобно управлять тысячами задач.
⭐️ ZGC / Shenandoah
Цель: сборщик мусора с минимальными паузами
Сборщики чуть отличаются по реализации, но задача одна — обеспечить минимум простоя во время сборки мусора. Разумеется, не бесплатно. Паузы уменьшаются, но увеличивается расход памяти и снижается пропускная способность.
Для большинства проектов это не актуально. Сборщик по умолчанию G1 отлично работает и становится лучше с каждым релизом.
⭐️ Project Panama
Цель: упростить работу с native кодом и памятью за пределами JVM
Проект делится на 2 направления:
🔹 Новый вариант JNI
Нужен для работы с библиотеками, которые не написаны на Java, и вряд ли когда-нибудь будут: работа с графикой, манипуляции с ОС, сетью и тд. Текущий JNI очень старый, работает медленно и не безопасен. Поэтому пишут новый:)
🔹 Работа с памятью за пределами JVM
Нужна проектам, которые хотят управлять памятью без посредничества Java. Самим делать сборку мусора, сжимать и раскладывать данные по определённым структурам.
⭐️ Project Amber
Цель: упростить язык, добавить новые конструкции
Самые "народные" фичи, которые часто попадают в обзоры и статьи:
var, текстовые блоки, records, pattern matching, sealed классы, string templates и так далее.Что-то получается хорошо, что-то не очень. Где-то много пафосных разговоров про data-oriented programming. Есть странные фичи, вроде упрощения написания Hello world.
⭐️ Project Leyden
Цель: ускорить время старта Java программ
Оптимизировать загрузку классов, линковку, перенести часть процессов на этап компиляции. На энтерпрайз повлияет мало, по сравнению с работой фреймворков ускорения на уровне JVM будут мало заметны.
⭐️ Project Valhalla
Цель: оптимизировать работу с данными
Здесь так же два направления:
🔹 Создать value types — объект с полями и методами, работа с которым идёт как с примитивом:
✅ Передаётся по значению
✅ Компактно лежит в памяти
✅ Не может быть null
🔹 Создать общую схему работы с примитивами, объектами и value types, избавить разработчика от мыслей про boxing/unboxing
❓ А когда будет готово?
Плохая новость — реализации всех проектов растягиваются на десятки лет.
10 лет — не преувеличение. Лямбда-выражения в java обсуждались с 2004 года, а увидели свет только в 2014.
В случае Java медлительность — это фича и часть стратегии: смотреть, как решаются проблемы в других языках и не изобретать велосипед. Осторожно выбирать, что войдёт в язык, а что — нет, тщательно продумывать архитектуру.
На java пишут большие системы, которые работают десятки лет. Поэтому основательный подход абсолютно оправдан😌
👍177🔥53❤35👎3
Есть ли у вас высшее айтишное образование?
Anonymous Poll
39%
Есть. Доволен, что получил
15%
Есть, но мог бы обойтись и без
30%
Нет, но думаю, что вышка полезна
17%
Нет, и не вижу смысла
👍21
Нужно ли высшее образование для разработчика?
Встречаются 2 радикальных мнения:
🧔♂️ Вышка не нужна, там старая программа. Всё есть в интернете и можно выучить самому
👩🦰 Только с высшим образованием станешь “нормальным” разработчиком, без вышки твой потолок — это круды
Абстрактно рассуждать можно долго, я расскажу свой личный опыт.
Я отучилась в ИТМО на инженера-программиста 11 лет назад. "Информационные технологии" присутствуют в названии универа, факультета и специальности — уровень айтишности максимальный.
Какие плюсы вижу с высоты прожитых лет:
✅ Можно попробовать разные стороны IT
Помимо разработки было море других предметов: кодирование данных, математическое моделирование, ассемблер, компьютерная графика и даже 1С:). Здорово расширяет горизонт и помогает нащупать, что интересно конкретно тебе.
Выбор самоучек ограничен энтерпрайзным меню: фронт, бэк или мобилки. Дальше человек ориентируется на требования рынка.
Подход практичный, но не даёт шанса влюбиться в биоинформатику или разработку компиляторов.
✅ Базовые знания
Как работают операционные системы, компьютерные сети, linux/bash и тд.
На курсах такому не учат, в вакансиях эти требования не прописаны. В итоге часто получаем сотрудника, который боится консоли и не выходит за пределы IDE.
Джунам это простительно — один раз покажешь, как смотреть логи на удалённой машине, и они запомнят. Но чем сеньористее становишься, тем важнее понимание общей картины, и тут пробелы становятся очень заметны.
Эти темы легко освоить самостоятельно, глубокого погружения не требуется. Но на практике редко у кого доходят руки до таких тем. В универе у тебя нет выбора:)
✅ Стажировки, факультативы, конкурсы, хакатоны
от университета и известных компаний. Учёба по обмену в другой стране. Интересные люди приходят читать лекции и делиться опытом.
Во "внешнем мире" тоже полно стажировок, лекций и хакатонов, но для студентов количество программ в разы выше.
А теперь минусы:
❌ Долго и непродуктивно
За 4-5 лет через студента проходит огромный объём информации, и большинство тем он видит в первый и последний раз. Ни ассемблер, ни теория поля мне ни разу не пригодились.
Весь объём знаний, который я использую сейчас, осваивается за полгода-год самостоятельного обучения.
❌ Знаний из университета недостаточно
Нет специальности "Java разработчик". В университете учат на универсального инженера-программиста. Поэтому уходить вглубь пришлось самостоятельно.
В универе рассказывали, что после выпуска нас "будут с руками отрывать топовые компании". Так вот, это неправда. Хотя сейчас во многих универах есть практика с джавой, спрингом и докером, этого недостаточно. Доучиваться всё равно придётся.
Но не всё так страшно. Мне хватило пары месяцев, чтобы подготовиться к собеседованиям и устроиться на работу. После косинусных преобразований и электротехники освоить технологии энтерпрайза очень просто:)
Итого
Хотя мне не пригодилось 90% того, что было в университете, сама учёба мне нравилась, и это было отличное время🥰
🔸 Если вам 15-18, интересно айти и есть время на учебу, рекомендую идти в универ и брать от него максимум. Ходить на факультативы, вписываться в стажировки и доп.программы, наращивать связи
🔸 Если чувствуете серьёзный интерес к области, далёкой от энтерпрайза, поищите соответствующую магистратуру. Разработка языков, биоинформатика, квантовые вычисления, etc — всё это лучше осваивать комплексно и в кругу единомышленников
🔸 Если вы уже работаете и видите, куда и как расти, то получать дополнительно высшее образование не нужно. Полезнее прицельно изучать технологии и качать софт-скиллы
Встречаются 2 радикальных мнения:
🧔♂️ Вышка не нужна, там старая программа. Всё есть в интернете и можно выучить самому
👩🦰 Только с высшим образованием станешь “нормальным” разработчиком, без вышки твой потолок — это круды
Абстрактно рассуждать можно долго, я расскажу свой личный опыт.
Я отучилась в ИТМО на инженера-программиста 11 лет назад. "Информационные технологии" присутствуют в названии универа, факультета и специальности — уровень айтишности максимальный.
Какие плюсы вижу с высоты прожитых лет:
✅ Можно попробовать разные стороны IT
Помимо разработки было море других предметов: кодирование данных, математическое моделирование, ассемблер, компьютерная графика и даже 1С:). Здорово расширяет горизонт и помогает нащупать, что интересно конкретно тебе.
Выбор самоучек ограничен энтерпрайзным меню: фронт, бэк или мобилки. Дальше человек ориентируется на требования рынка.
Подход практичный, но не даёт шанса влюбиться в биоинформатику или разработку компиляторов.
✅ Базовые знания
Как работают операционные системы, компьютерные сети, linux/bash и тд.
На курсах такому не учат, в вакансиях эти требования не прописаны. В итоге часто получаем сотрудника, который боится консоли и не выходит за пределы IDE.
Джунам это простительно — один раз покажешь, как смотреть логи на удалённой машине, и они запомнят. Но чем сеньористее становишься, тем важнее понимание общей картины, и тут пробелы становятся очень заметны.
Эти темы легко освоить самостоятельно, глубокого погружения не требуется. Но на практике редко у кого доходят руки до таких тем. В универе у тебя нет выбора:)
✅ Стажировки, факультативы, конкурсы, хакатоны
от университета и известных компаний. Учёба по обмену в другой стране. Интересные люди приходят читать лекции и делиться опытом.
Во "внешнем мире" тоже полно стажировок, лекций и хакатонов, но для студентов количество программ в разы выше.
А теперь минусы:
❌ Долго и непродуктивно
За 4-5 лет через студента проходит огромный объём информации, и большинство тем он видит в первый и последний раз. Ни ассемблер, ни теория поля мне ни разу не пригодились.
Весь объём знаний, который я использую сейчас, осваивается за полгода-год самостоятельного обучения.
❌ Знаний из университета недостаточно
Нет специальности "Java разработчик". В университете учат на универсального инженера-программиста. Поэтому уходить вглубь пришлось самостоятельно.
В универе рассказывали, что после выпуска нас "будут с руками отрывать топовые компании". Так вот, это неправда. Хотя сейчас во многих универах есть практика с джавой, спрингом и докером, этого недостаточно. Доучиваться всё равно придётся.
Но не всё так страшно. Мне хватило пары месяцев, чтобы подготовиться к собеседованиям и устроиться на работу. После косинусных преобразований и электротехники освоить технологии энтерпрайза очень просто:)
Итого
Хотя мне не пригодилось 90% того, что было в университете, сама учёба мне нравилась, и это было отличное время🥰
🔸 Если вам 15-18, интересно айти и есть время на учебу, рекомендую идти в универ и брать от него максимум. Ходить на факультативы, вписываться в стажировки и доп.программы, наращивать связи
🔸 Если чувствуете серьёзный интерес к области, далёкой от энтерпрайза, поищите соответствующую магистратуру. Разработка языков, биоинформатика, квантовые вычисления, etc — всё это лучше осваивать комплексно и в кругу единомышленников
🔸 Если вы уже работаете и видите, куда и как расти, то получать дополнительно высшее образование не нужно. Полезнее прицельно изучать технологии и качать софт-скиллы
🔥135👍99❤39👎2
Fluent API: что такое и зачем нужен
Fluent API набирает популярность последние лет 5 и часто вызывает вопросы:
🤔 В чём его польза?
🤔 Чем он отличается от билдера?
🤔 Когда нужен, а когда нет?
В этом и следующем посте постараюсь ответить на эти вопросы.
Основная задача Fluent API — улучшить читаемость кода. Для этого:
🔸 Несколько методов объединяются в цепочку и вызываются друг за другом. Получается логический блок, который воспринимается как единое целое
🔸 Названия методов не ограничиваются глаголом
Пример:
Fluent API не всегда уместен и не всегда реализован удачно. Список формальных условий определить сложно, на практике такой формат часто выбирается интуитивно. Поэтому рассмотрим побольше примеров!
✅ Хороший пример из AssertJ:
За раз пишем несколько проверок для строки str. Без Fluent API кода будет больше
✅ Хороший пример из Мокито:
Читается как одно предложение. Мне даже сложно представить, как это написать в “традиционном” стиле:)
✅ Прекрасный пример из Spring JDBC:
Почему пример прекрасен? Потому что Fluent API скрывает работу с объектом PreparedStatement. Код получается не только короче, но и проще🔥
😐 Так себе пример из Spring Data:
Приставка with у методов лишняя, название последнего метода неудачное
❌ Плохой пример из SLF4J:
Классический
❌ Плохой пример из популярного джава канала:
Тоже никакой пользы от Fluent API, ни по читаемости, ни по удобству использования.
Итого
Fluent API подойдёт, когда работа с объектом проходит в несколько шагов, но единым логическим блоком. Основная цель — улучшить читаемость. Высший пилотаж — повысить с помощью Fluent API уровень инкапсуляции.
Чтобы сделать удобно и красиво, нужен опыт, насмотренность и немножко вдохновения:)
Отдельный случай — Fluent API при создании объектов. Здесь часто возникает путаница с билдером, и непонятно, что когда использовать. Этот вопрос я разберу отдельно в следующем посте🔥
Fluent API набирает популярность последние лет 5 и часто вызывает вопросы:
🤔 В чём его польза?
🤔 Чем он отличается от билдера?
🤔 Когда нужен, а когда нет?
В этом и следующем посте постараюсь ответить на эти вопросы.
Основная задача Fluent API — улучшить читаемость кода. Для этого:
🔸 Несколько методов объединяются в цепочку и вызываются друг за другом. Получается логический блок, который воспринимается как единое целое
🔸 Названия методов не ограничиваются глаголом
Пример:
CompletableFuture.runAsync(…).thenRun(…).exceptionally(…);
Fluent API не всегда уместен и не всегда реализован удачно. Список формальных условий определить сложно, на практике такой формат часто выбирается интуитивно. Поэтому рассмотрим побольше примеров!
✅ Хороший пример из AssertJ:
assertThat(str).startsWith(…).contains(…);
За раз пишем несколько проверок для строки str. Без Fluent API кода будет больше
✅ Хороший пример из Мокито:
when(mock.method()).thenReturn(…).thenReturn(…).thenThrow(…);
Читается как одно предложение. Мне даже сложно представить, как это написать в “традиционном” стиле:)
✅ Прекрасный пример из Spring JDBC:
List<User > users = jdbcClient.sql(…)
.param("rating", 5, Types.INTEGER)
.query(mapper)
.list();
Почему пример прекрасен? Потому что Fluent API скрывает работу с объектом PreparedStatement. Код получается не только короче, но и проще🔥
😐 Так себе пример из Spring Data:
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths(…)
.withStringMatcher(StringMatcher.ENDING);
Приставка with у методов лишняя, название последнего метода неудачное
❌ Плохой пример из SLF4J:
logger.atInfo().log(…);
Классический
logger.info(…) короче и удобнее❌ Плохой пример из популярного джава канала:
Person person = new Person().setName(…).setAge(…);
Тоже никакой пользы от Fluent API, ни по читаемости, ни по удобству использования.
Итого
Fluent API подойдёт, когда работа с объектом проходит в несколько шагов, но единым логическим блоком. Основная цель — улучшить читаемость. Высший пилотаж — повысить с помощью Fluent API уровень инкапсуляции.
Чтобы сделать удобно и красиво, нужен опыт, насмотренность и немножко вдохновения:)
Отдельный случай — Fluent API при создании объектов. Здесь часто возникает путаница с билдером, и непонятно, что когда использовать. Этот вопрос я разберу отдельно в следующем посте🔥
🔥134👍65❤21👎6
Что лучше — Fluent API или Builder?
Продолжим разговор о Fluent API и рассмотрим частный случай — создание объекта. Сравним "классический" флуент апи с билдером и посмотрим, когда что использовать.
Вопрос очень актуален, так как не раз приходилось созваниваться и объяснять коллегам это всё словами:) Теперь буду кидать ссылку на пост.
Уточним термины
▫️
▫️
В чём разница на практике?
Допустим, надо создать экземпляр класса Account с информацией о счёте. Рассмотрим 4 варианта:
1️⃣ Все параметры передаём в конструкторе:
✅ Идеально для обязательных параметров. Компилятор не даст шанса пользователю что-то забыть
❌ Плохая читаемость — передаётся набор чисел, непонятно, что есть что
❌ Если есть необязательные параметры, нужно несколько конструкторов
2️⃣ Параметры выставляем через сеттеры:
✅ Читаемость лучше. Понятно, с какими полями работаем
❌ Нет контроля за обязательными параметрами. Пользователь должен знать, какие сеттеры вызвать обязательно, а какие нет
3️⃣ Используем Fluent API:
✅ Симпатичнее, чем сеттеры
❌ Та же проблема с обязательными параметрами, никаких проверок
❌ Не подходит для неизменяемых классов
❌ Более сложный код. Но если у вас разрешён ломбок, аннотация
4️⃣ Создание объекта через билдер:
✅ В build() можно добавить нужные проверки
✅ Можно использовать для неизменяемых классов
❌ Надо написать отдельный класс Builder или использовать аннотацию
Создание объекта через Fluent API не очень отличается от билдера по лаконичности, но проигрывает ему в функциональности. Fluent API не может проверить поля на "обязательность".
Итого
🔸 Если в классе мало полей, идём по классическому пути — обязательные параметры помещаем в конструктор, необязательные выставляем сеттерами
🔸 Если полей много или между полями сложные связи — используем билдер. Именованые методы улучшат читаемость, а метод build() проверит всё, что нужно
Fluent API обычно не даёт преимуществ в ситуациях выше, и редко используется для создания объектов. Его сильная сторона — логические блоки из нескольких операций🔥
Продолжим разговор о Fluent API и рассмотрим частный случай — создание объекта. Сравним "классический" флуент апи с билдером и посмотрим, когда что использовать.
Вопрос очень актуален, так как не раз приходилось созваниваться и объяснять коллегам это всё словами:) Теперь буду кидать ссылку на пост.
Уточним термины
▫️
Fluent API — это стиль написания кода. Грубо говоря, когда методы соединяются через точку (method chaining). С помощью Fluent API можно создать объект и проинициализировать поля.▫️
Builder — паттерн для создания объектов, суть которого в постепенном накоплении информации. Билдер тоже использует method chaining и относится к fluent api. В чём разница на практике?
Допустим, надо создать экземпляр класса Account с информацией о счёте. Рассмотрим 4 варианта:
1️⃣ Все параметры передаём в конструкторе:
Account acc = new Account(111, 222, 333);
✅ Идеально для обязательных параметров. Компилятор не даст шанса пользователю что-то забыть
❌ Плохая читаемость — передаётся набор чисел, непонятно, что есть что
❌ Если есть необязательные параметры, нужно несколько конструкторов
2️⃣ Параметры выставляем через сеттеры:
Account acc = new Account();
acc.setINN(111);
acc.setKPP(222);
acc.setRCBIC(333);
✅ Читаемость лучше. Понятно, с какими полями работаем
❌ Нет контроля за обязательными параметрами. Пользователь должен знать, какие сеттеры вызвать обязательно, а какие нет
3️⃣ Используем Fluent API:
Account acc = Account.new()
.INN(111)
.KPP(222)
.RCBIC(333);
✅ Симпатичнее, чем сеттеры
❌ Та же проблема с обязательными параметрами, никаких проверок
❌ Не подходит для неизменяемых классов
❌ Более сложный код. Но если у вас разрешён ломбок, аннотация
@Accessors(chain = true) упрощает задачу4️⃣ Создание объекта через билдер:
Account acc = Account.builder().
.INN(111)
.KPP(222)
.RCBIC(333)
.build();
✅ В build() можно добавить нужные проверки
✅ Можно использовать для неизменяемых классов
❌ Надо написать отдельный класс Builder или использовать аннотацию
@Builder из ломбокаСоздание объекта через Fluent API не очень отличается от билдера по лаконичности, но проигрывает ему в функциональности. Fluent API не может проверить поля на "обязательность".
Итого
🔸 Если в классе мало полей, идём по классическому пути — обязательные параметры помещаем в конструктор, необязательные выставляем сеттерами
🔸 Если полей много или между полями сложные связи — используем билдер. Именованые методы улучшат читаемость, а метод build() проверит всё, что нужно
Fluent API обычно не даёт преимуществ в ситуациях выше, и редко используется для создания объектов. Его сильная сторона — логические блоки из нескольких операций🔥
🔥120👍56❤11👎7
Паттерн Builder для продвинутых
Начав обсуждать билдер, остановиться сложно. Я и не буду:) Сегодня подсвечу пару интересных моментов и один приём, который точно пригодится на практике.
Сразу к делу:
1️⃣ Билдер может содержать сложную логику, а не просто копить будущие поля
Например:
🔸 Собирать одну информацию, отдавать объекту другую
🔸 Возвращать любые классы и объекты, даже прокси и уже созданные
🔸 Проводить операции внутри метода
Всё это при умелом использовании создаёт симпатичное и лаконичное API.
Пример 1: класс
Гораздо интереснее, чем кажется:
▫️ Копит данные в изменяемом массиве, на выходе отдаёт неизменяемую строку
▫️ Методы
▫️ Есть дополнительные методы вроде
В результате получаем удобный и функциональный класс. Ломбок такой билдер не соберёт, чатЖПТ такое не придумает:)
Пример 2: класс HttpClient
В самом простом варианте код выглядит так:
🤔 Зачем тут билдер? Почему просто не сделать new HttpClient()?
Потому что внутри build происходит такая магия:
Тут прячется установка работы с сетью в 200+ строк кода и механизм по завершению работы с сетью, когда объект HttpClient станет не нужен. Хотя объект работает с ресурсами, его не надо помещать в блок
Мелочь, а приятно🥰
И вторая классная особенность:
2️⃣ Билдер может сделать несколько объектов на базе переданных данных
Часто помогает при написании тестов.
Пример: тестируем работу с классом Account, в котором много полей. Можно в каждом тесте создавать тестовые объекты с нуля и копипастить километр кода. Или поступить иначе:
▫️ Сделать общий для всех тестов билдер, но build() не вызывать
▫️ В каждом тесте доставить нужные поля и построить новый объект
Получается так:
Это короче, чем в каждом тесте создавать объект с нуля. Плюс сразу видно "главное" для теста поле.
Приём подходит не всегда, но иногда здорово улучшает читаемость.
Итого
✅ Метод
✅ Builder может создать несколько объектов на основе переданных данных
Возьмите эти свойства на заметку, в умелых руках паттерн Builder делает код проще и удобнее🔥
Начав обсуждать билдер, остановиться сложно. Я и не буду:) Сегодня подсвечу пару интересных моментов и один приём, который точно пригодится на практике.
Сразу к делу:
1️⃣ Билдер может содержать сложную логику, а не просто копить будущие поля
Например:
🔸 Собирать одну информацию, отдавать объекту другую
🔸 Возвращать любые классы и объекты, даже прокси и уже созданные
🔸 Проводить операции внутри метода
buildВсё это при умелом использовании создаёт симпатичное и лаконичное API.
Пример 1: класс
StringBuilder Гораздо интереснее, чем кажется:
▫️ Копит данные в изменяемом массиве, на выходе отдаёт неизменяемую строку
▫️ Методы
append можно вызывать сколько угодно раз▫️ Есть дополнительные методы вроде
reverse или deleteВ результате получаем удобный и функциональный класс. Ломбок такой билдер не соберёт, чатЖПТ такое не придумает:)
Пример 2: класс HttpClient
В самом простом варианте код выглядит так:
HttpClient client = HttpClient.newBuilder().build();
🤔 Зачем тут билдер? Почему просто не сделать new HttpClient()?
Потому что внутри build происходит такая магия:
SingleFacadeFactory facadeFactory = new SingleFacadeFactory();
HttpClientImpl impl = new HttpClientImpl(builder, facadeFactory);
impl.start();
…
return facadeFactory.facade;
Тут прячется установка работы с сетью в 200+ строк кода и механизм по завершению работы с сетью, когда объект HttpClient станет не нужен. Хотя объект работает с ресурсами, его не надо помещать в блок
try-with-resources.Мелочь, а приятно🥰
И вторая классная особенность:
2️⃣ Билдер может сделать несколько объектов на базе переданных данных
Часто помогает при написании тестов.
Пример: тестируем работу с классом Account, в котором много полей. Можно в каждом тесте создавать тестовые объекты с нуля и копипастить километр кода. Или поступить иначе:
▫️ Сделать общий для всех тестов билдер, но build() не вызывать
▫️ В каждом тесте доставить нужные поля и построить новый объект
Получается так:
// общее поле c базовой информацией
Account.Builder accBuilder = Account.builder().INN(…).KPP(…)
// в тесте, где важен БИК:
Account acc = accBuilder.RCBIC(123).build();
// в тесте, где нужно название банка
Acccount acc = accBuilder.bankName("green").build();
Это короче, чем в каждом тесте создавать объект с нуля. Плюс сразу видно "главное" для теста поле.
Приём подходит не всегда, но иногда здорово улучшает читаемость.
Итого
✅ Метод
build может содержать сложную логику: от проверки параметров до шифрований и преобразований✅ Builder может создать несколько объектов на основе переданных данных
Возьмите эти свойства на заметку, в умелых руках паттерн Builder делает код проще и удобнее🔥
👍152🔥75❤23👎2
👩🏫Чему поучиться
Посты это прекрасный, но очень сжатый формат. Для тех, кто хочет большего:
1️⃣ Бот для подготовки к собеседованиям и прокачки навыков
Java, Spring, Kafka, Postgres, микросервисы и всё, что требует рынок от Middle/Senior разработчика
2️⃣ Курс Основы многопоточности
Бесплатный!
Его задача — пройтись по базовым темам многопоточки и заполнить пробелы, если они есть:
▫️ Класс Thread
▫️ Экзекьюторы
▫️ Основные проблемы в многопоточной среде
▫️ Модификатор volatile
▫️ Ключевое слово synchronized
Темы не раскрыты даже на 50%, многие вещи сильно упрощены. Но чтобы понять базу и пройти несложные собеседования — самое то.
3️⃣ Курс Многопоточное программирование на Java
Самый полный и практичный курс по java.util.concurrent и смежным темам🔥
Разбираемся, как писать эффективный и быстрый код и готовимся к собеседованиям. Изучаем best practices и популярные ошибки.
Самая мощная часть — это практика:
✅ Решаем типичные энтерпрайзные задачи
✅ Анализируем многопоточный код Spring, Kafka, Hadoop и других популярных фреймворков — смотрим интересные приёмы и ищем ошибки
✅ Сравниваем разные решения и оцениваем производительность
Такого вы не найдёте нигде. Курс не пересказывает документацию и Concurrency in practice. Он о том, как применять многопоточку в реальной жизни.
Курс нацелен на самостоятельное прохождение, для всех заданий есть подсказки, инструкции для самопроверки и разбор важных нюансов. Я вела курс с обратной связью 3 года и прекрасно понимаю, где возникают сложности.
Если что-то совсем непонятно, можно у меня спросить:)
Что ещё:
▫️ Прохождение занимает 2-3 месяца
▫️ Доступ сразу после оплаты
▫️ Есть рассрочка и оплата за счёт компании
▫️ Можно оформить налоговый вычет и вернуть в следующем году 13% стоимости
Почитать программу, отзывы и записаться → https://fillthegaps.ru/mt
Посты это прекрасный, но очень сжатый формат. Для тех, кто хочет большего:
1️⃣ Бот для подготовки к собеседованиям и прокачки навыков
Java, Spring, Kafka, Postgres, микросервисы и всё, что требует рынок от Middle/Senior разработчика
2️⃣ Курс Основы многопоточности
Бесплатный!
Его задача — пройтись по базовым темам многопоточки и заполнить пробелы, если они есть:
▫️ Класс Thread
▫️ Экзекьюторы
▫️ Основные проблемы в многопоточной среде
▫️ Модификатор volatile
▫️ Ключевое слово synchronized
Темы не раскрыты даже на 50%, многие вещи сильно упрощены. Но чтобы понять базу и пройти несложные собеседования — самое то.
3️⃣ Курс Многопоточное программирование на Java
Самый полный и практичный курс по java.util.concurrent и смежным темам🔥
Разбираемся, как писать эффективный и быстрый код и готовимся к собеседованиям. Изучаем best practices и популярные ошибки.
Самая мощная часть — это практика:
✅ Решаем типичные энтерпрайзные задачи
✅ Анализируем многопоточный код Spring, Kafka, Hadoop и других популярных фреймворков — смотрим интересные приёмы и ищем ошибки
✅ Сравниваем разные решения и оцениваем производительность
Такого вы не найдёте нигде. Курс не пересказывает документацию и Concurrency in practice. Он о том, как применять многопоточку в реальной жизни.
Курс нацелен на самостоятельное прохождение, для всех заданий есть подсказки, инструкции для самопроверки и разбор важных нюансов. Я вела курс с обратной связью 3 года и прекрасно понимаю, где возникают сложности.
Если что-то совсем непонятно, можно у меня спросить:)
Что ещё:
▫️ Прохождение занимает 2-3 месяца
▫️ Доступ сразу после оплаты
▫️ Есть рассрочка и оплата за счёт компании
▫️ Можно оформить налоговый вычет и вернуть в следующем году 13% стоимости
Почитать программу, отзывы и записаться → https://fillthegaps.ru/mt
🔥50👍30❤16
На основе чего по умолчанию считается хэшкод объекта?
Anonymous Poll
22%
Случайное число от генератора внутри JVM
3%
Случайное число от операционной системы
64%
Адрес объекта в памяти
5%
Внутренние переменные потока
3%
Время создания объекта
2%
Последовательность по возрастанию
👍26❤5
Как считается хэшкод по умолчанию?
На собеседованиях часто обсуждают методы
Если хочется посмотреть, как думает человек за пределами стандартного ответа, возможен такой диалог:
— Как считается хэшкод по умолчанию?
— Это адрес объекта в памяти
— А почему так?
— Адрес каждого объекта уникален, то что надо для хэшкода
— Сборщик мусора перемещает объекты внутри памяти. Как это влияет на значения хэшей?
— 😥
Тут можно предположить, что хэшкод считается один раз и приписывается к самому объекту. Это будет логичная мысль.
А вот вычисление хэша на основе адреса в памяти — популярный миф. В этом посте разберём, как на самом считается хэшкод под умолчанию.
В разных JVM реализации могут отличаться. Рассмотрим исходный код hashcode в OpenJDK. Там 6(!) стратегий вычисления хэшкода. Стратегия задаётся опциями VM:
При первом вызове хэш сохраняется внутри объекта и не меняется. Теперь к стратегиям:
🔸-XX:hashCode=0
Случайное число по алгоритму Lehmer RNG. Генератор один на всех, поэтому работает медленно
🔸-XX:hashCode=2
Чемпион по скорости, всегда возвращает 1:
Используется как отправная точка для тестов остальных стратегий
🔸-XX:hashCode=3
Обычная возрастающая последовательность:
🔸-XX:hashCode=4
Текущий адрес в памяти. Популярный, но неправильный ответ на собеседованиях. Отчасти в этом виновата спецификация: там адрес приводится как пример реализации. Работает быстро, но не даёт равномерного распределения и должного уровня уникальности
🔸-XX:hashCode=1
Адрес объекта в памяти и немного манипуляций с битами
🔸 Стратегия по умолчанию
Случайное число по алгоритму Xorshift RNG. Следующее значение вычисляется на основе предыдущего. Значения равномерно распределены. Работает быстро, тк у каждого потока свой генератор, и синхронизации между потоками нет
Рейтинг стратегий по скорости:
🏆 Вернуть единицу: 184 операций за микросекунду
🥈 Вариант по умолчанию: 176 оп/мск
🥉 Адрес в памяти-1: 160 оп/мск
▪️ Растущая последовательность: 14 оп/мск
▪️ Случайное число и глобальная переменная: 10 оп/мск
Интересно, что до java 8 самая медленная опция была вариантом по умолчанию.
Итого
✅ Реализация хэшкода зависит от JVM и VM-флажков
✅ В OpenJDK 6 стратегий вычисления хэшкода. По умолчанию используется генератор случайных чисел в рамках одного потока
✅ Расчёт на основе адреса памяти не очень хорош по итоговым характеристикам
✅ Общие переменные в методах снижают производительность при интенсивном использовании. Яркие примеры — стратегии хэшкода с общим генератором и последовательностью
На собеседованиях часто обсуждают методы
equals и hashcode. За что отвечают, как соотносятся между собой, когда переопределять, а когда не стоит.Если хочется посмотреть, как думает человек за пределами стандартного ответа, возможен такой диалог:
— Как считается хэшкод по умолчанию?
— Это адрес объекта в памяти
— А почему так?
— Адрес каждого объекта уникален, то что надо для хэшкода
— Сборщик мусора перемещает объекты внутри памяти. Как это влияет на значения хэшей?
— 😥
Тут можно предположить, что хэшкод считается один раз и приписывается к самому объекту. Это будет логичная мысль.
А вот вычисление хэша на основе адреса в памяти — популярный миф. В этом посте разберём, как на самом считается хэшкод под умолчанию.
В разных JVM реализации могут отличаться. Рассмотрим исходный код hashcode в OpenJDK. Там 6(!) стратегий вычисления хэшкода. Стратегия задаётся опциями VM:
-XX:+UnlockExperimentalVMOptions -XX:hashCode={число}При первом вызове хэш сохраняется внутри объекта и не меняется. Теперь к стратегиям:
🔸-XX:hashCode=0
Случайное число по алгоритму Lehmer RNG. Генератор один на всех, поэтому работает медленно
🔸-XX:hashCode=2
Чемпион по скорости, всегда возвращает 1:
java.lang.Object@1
Используется как отправная точка для тестов остальных стратегий
🔸-XX:hashCode=3
Обычная возрастающая последовательность:
java.lang.Object@a4
java.lang.Object@a5
java.lang.Object@a6
🔸-XX:hashCode=4
Текущий адрес в памяти. Популярный, но неправильный ответ на собеседованиях. Отчасти в этом виновата спецификация: там адрес приводится как пример реализации. Работает быстро, но не даёт равномерного распределения и должного уровня уникальности
🔸-XX:hashCode=1
Адрес объекта в памяти и немного манипуляций с битами
🔸 Стратегия по умолчанию
Случайное число по алгоритму Xorshift RNG. Следующее значение вычисляется на основе предыдущего. Значения равномерно распределены. Работает быстро, тк у каждого потока свой генератор, и синхронизации между потоками нет
Рейтинг стратегий по скорости:
🏆 Вернуть единицу: 184 операций за микросекунду
🥈 Вариант по умолчанию: 176 оп/мск
🥉 Адрес в памяти-1: 160 оп/мск
▪️ Растущая последовательность: 14 оп/мск
▪️ Случайное число и глобальная переменная: 10 оп/мск
Интересно, что до java 8 самая медленная опция была вариантом по умолчанию.
Итого
✅ Реализация хэшкода зависит от JVM и VM-флажков
✅ В OpenJDK 6 стратегий вычисления хэшкода. По умолчанию используется генератор случайных чисел в рамках одного потока
✅ Расчёт на основе адреса памяти не очень хорош по итоговым характеристикам
✅ Общие переменные в методах снижают производительность при интенсивном использовании. Яркие примеры — стратегии хэшкода с общим генератором и последовательностью
🔥220👍81❤27👎4
В каких методах НЕправильно используется Optional?
Anonymous Poll
6%
Optional<Student> get(long id)
66%
Optional<List<Student>> all()
65%
List<Student> search(Optional<String> city)
62%
void init(Optional<String> conf)
10%
Optional<String> getAddress()
👍11❤2