Switch: успеть до 30-ти
Сегодняшняя тема - история успеха оператора switch. Он появился в java 1.0, и с тех пор оставался в неизменном виде. В 2018 разработчики JDK смахнули пыль с кодовой базы switch, и теперь над ним идёт активная работа.
Почему о нём вспомнили, и как меняется switch? Рассмотрим по порядку.
1️⃣ Часть 1. Эпоха ООП
Java работает с объектами уже 25 лет. В этих условиях switch редко встречается в коде и часто считается плохой практикой.
Почему? Всё дело в сценарии использования:
Почему switch так себе вариант?
❌Сложный код. Работа со всеми состояниями в одной куче.
❌Дублирование кода. Если поле проверяется несколько раз, то менять такой код неудобно и легко ошибиться.
Для объекта с понятным набором состояний switch лучше заменить на полиморфные методы. Это несложный рефакторинг - пример1, пример2.
Цель ООП - смоделировать реальный мир через объекты. Главное здесь - объекты взаимодействуют и меняют состояние друг друга. В таких условиях switch проигрывает полиморфным методам и редко используется.
2️⃣ Часть 2. Эра функциональности
Сегодня для бизнеса недостаточно простой автоматизации. Основной задачей становится работа с данными.
Они не меняются, приходят из разных источников в разных форматах. Потоки данных идут через множество сервисов. Каждый сервис берёт данные, которые понимает, а остальные игнорирует. Строить иерархии классов для такой задачи кажется лишним и сложным.
Поэтому внедряются подходы из фунциональных языков. Один из них - pattern matching, а switch идеально подходит для его реализации. Паттерн - некоторое условие для переменной:
▫️Равна заданной константе
▫️Имеет определённый тип
▫️Подходит под регулярное выражение
Если произошёл мэтч, то для переменной сразу доступна доп.информация. Например, она приводится к нужному типу:
Switch 2000 работает с объектами, у которых меняется состояние. Вокруг этого строится бизнес-логика.
Switch 2021 работает с неизменными данными и помогает найти среди них подходящие. В следующем году выйдет java 17, и switch будет появляться в коде чаще.
Вот так один непопулярный оператор в JDK нашёл своё место в мире спустя 23 года⭐️
Сегодняшняя тема - история успеха оператора switch. Он появился в java 1.0, и с тех пор оставался в неизменном виде. В 2018 разработчики JDK смахнули пыль с кодовой базы switch, и теперь над ним идёт активная работа.
Почему о нём вспомнили, и как меняется switch? Рассмотрим по порядку.
1️⃣ Часть 1. Эпоха ООП
Java работает с объектами уже 25 лет. В этих условиях switch редко встречается в коде и часто считается плохой практикой.
Почему? Всё дело в сценарии использования:
switch (user.getState()) {
case NEW: …
case CONFIRMED: …
case BANNED: … }
Switch - это не просто набор нескольких if. У объекта user 3 статуса. Список чётко определен, статусы не пересекаются между собой.Почему switch так себе вариант?
❌Сложный код. Работа со всеми состояниями в одной куче.
❌Дублирование кода. Если поле проверяется несколько раз, то менять такой код неудобно и легко ошибиться.
Для объекта с понятным набором состояний switch лучше заменить на полиморфные методы. Это несложный рефакторинг - пример1, пример2.
Цель ООП - смоделировать реальный мир через объекты. Главное здесь - объекты взаимодействуют и меняют состояние друг друга. В таких условиях switch проигрывает полиморфным методам и редко используется.
2️⃣ Часть 2. Эра функциональности
Сегодня для бизнеса недостаточно простой автоматизации. Основной задачей становится работа с данными.
Они не меняются, приходят из разных источников в разных форматах. Потоки данных идут через множество сервисов. Каждый сервис берёт данные, которые понимает, а остальные игнорирует. Строить иерархии классов для такой задачи кажется лишним и сложным.
Поэтому внедряются подходы из фунциональных языков. Один из них - pattern matching, а switch идеально подходит для его реализации. Паттерн - некоторое условие для переменной:
▫️Равна заданной константе
▫️Имеет определённый тип
▫️Подходит под регулярное выражение
Если произошёл мэтч, то для переменной сразу доступна доп.информация. Например, она приводится к нужному типу:
switch (animal) {
case Cat c → c.putToBox();
case Dog d → d.train(); }
Итак, в чём разница между switch в 2000 и switch в 2021?Switch 2000 работает с объектами, у которых меняется состояние. Вокруг этого строится бизнес-логика.
Switch 2021 работает с неизменными данными и помогает найти среди них подходящие. В следующем году выйдет java 17, и switch будет появляться в коде чаще.
Вот так один непопулярный оператор в JDK нашёл своё место в мире спустя 23 года⭐️
👍2
Спасибо за этот год!
Завтра новый год, это отличный повод сказать нечто важное.
Ребята, вы супер! Спасибо, что читаете мои нудные посты без картинок, помогаете найти ошибки и задаёте интересные вопросы. Благодаря вам блог ещё жив❤️
В 2020 году на канале вышло 85 постов, которые в сумме набрали 475к просмотров! Я в шоке и постараюсь в 2021 не сбавлять обороты.
Желаю всем в следующем году +1 грейд, интересные проекты и яркую жизнь вне работы🔥
Завтра новый год, это отличный повод сказать нечто важное.
Ребята, вы супер! Спасибо, что читаете мои нудные посты без картинок, помогаете найти ошибки и задаёте интересные вопросы. Благодаря вам блог ещё жив❤️
В 2020 году на канале вышло 85 постов, которые в сумме набрали 475к просмотров! Я в шоке и постараюсь в 2021 не сбавлять обороты.
Желаю всем в следующем году +1 грейд, интересные проекты и яркую жизнь вне работы🔥
Гороскоп на 2021
Всем известно, что астрология играет важную роль в IT. Двухнедельный спринт - это ровно половина лунного цикла. Идеальный размер команды равен количеству планет солнечной системы. Премии рассчитываются по астральным коэффициентам.
Вот что говорят звёзды про 2021 год:
♈️ Овен
Металлический Бык симпатизирует Овнам, поэтому все инициативы будут удачны, особенно зимой, весной, летом и осенью. Будьте активны на ретро, предлагайте новые фичи и подходы, возьмите под наставничество стажёров. В сентябре ожидайте наплыв писем от HR.
♉️ Телец
В год Быка Тельцы нацелены на быстрый карьерный взлёт. Подтяните пробелы и обсудите с тимлидом возможности роста. В этом году звёзды раскрутили ваш потенциал до максимума. В августе будьте осторожнее с
♊️ Близнецы
Год будет спокойным и приятным. В прошлом году вы много работали, в 2021 выделяйте больше времени на отдых. Качество жизни и работы только улучшится. Самое время взяться за большие и фундаментальные книги, которые вы долго откладывали.
♋️ Рак
Откажитесь от лишней эмоциональности, она может помешать вашему развитию. Подтяните DevOps, в этом году он вам пригодится. Сложные задачи ждут вас в середине лета, но они дадут нужный стимул для дальнейшего роста.
♌️ Лев
В этом году удача не на вашей стороне, придётся много работать. Хотите успеха — начните сейчас, чтобы уже весной видеть первые результаты. Смотрите на вещи шире. Почитайте книжки по архитектуре, посмотрите видео с конференций HighLoad и ArchDays. В апреле высокий риск простудиться, одевайтесь теплее.
♍️ Дева
Год будет богат на свежие идеи и начинания. Начните то, что давно откладывали. Интересные идеи, вопросы и решения придут в самый неожиданный момент. Сохраняйте их сразу - запишите в блокнот, голосовое сообщение, как угодно, а то улетят. Обязательно делайте бэкапы и резервные копии.
♎️ Весы
В этом году у Весов будет шанс попробовать себя в руководящей роли. Готовьтесь заранее - почитайте статьи по управлению людьми и проектами. Подтяните тайм-менеджмент, иначе времени на хобби и внерабочие активности совсем не останется.
♏️ Скорпион
В этом году вы будете на переднем фронте. Вас ждут горячие фиксы и спасение команды перед дедлайном. Будет сложно, но Сатурн вам поможет. Помните об отдыхе и набирайтесь сил в спокойное время.
♐️ Стрелец
Пересмотрите приоритеты в жизни, попробуйте смежные IT направления. Возможно позиция тимлида, менеджера или аналитика раскроют вас с новой стороны. В этом году особую важность приобретут межличностные отношения. Октябрь станет самым прибыльным месяцем в году.
♑️ Козерог
Для вас 2021 год — это борьба со своими слабостями. Уделяйте больше внимания тестированию и самопроверке. Разберитесь с NoSQL: книга для начинающих, для продолжающих. Хорошей идеей будет сходить на каток и покататься на ватрушках.
♒️ Водолей
Лучшее время для решительных шагов — начало весны. Много возможностей принесёт нетворкинг - поддерживайте тёплые отношения с коллегами, участвуйте в конференциях, митапах и корпоративных мероприятиях. Идите в ногу со временем - освойте Kotlin и Cloud computing.
♓️ Рыбы
Наступает период, когда пора применить все накопленные знания. А возможности для этого обязательно будут. Меркурий помешает сделать важные задачи в срок, поэтому закладывайте на выполнение в 2 раза больше времени. Лето подкинет массу интересных вариантов для отдыха.
Дружите со звёздами, они плохого не посоветуют💫
Всем известно, что астрология играет важную роль в IT. Двухнедельный спринт - это ровно половина лунного цикла. Идеальный размер команды равен количеству планет солнечной системы. Премии рассчитываются по астральным коэффициентам.
Вот что говорят звёзды про 2021 год:
♈️ Овен
Металлический Бык симпатизирует Овнам, поэтому все инициативы будут удачны, особенно зимой, весной, летом и осенью. Будьте активны на ретро, предлагайте новые фичи и подходы, возьмите под наставничество стажёров. В сентябре ожидайте наплыв писем от HR.
♉️ Телец
В год Быка Тельцы нацелены на быстрый карьерный взлёт. Подтяните пробелы и обсудите с тимлидом возможности роста. В этом году звёзды раскрутили ваш потенциал до максимума. В августе будьте осторожнее с
git push --force.♊️ Близнецы
Год будет спокойным и приятным. В прошлом году вы много работали, в 2021 выделяйте больше времени на отдых. Качество жизни и работы только улучшится. Самое время взяться за большие и фундаментальные книги, которые вы долго откладывали.
♋️ Рак
Откажитесь от лишней эмоциональности, она может помешать вашему развитию. Подтяните DevOps, в этом году он вам пригодится. Сложные задачи ждут вас в середине лета, но они дадут нужный стимул для дальнейшего роста.
♌️ Лев
В этом году удача не на вашей стороне, придётся много работать. Хотите успеха — начните сейчас, чтобы уже весной видеть первые результаты. Смотрите на вещи шире. Почитайте книжки по архитектуре, посмотрите видео с конференций HighLoad и ArchDays. В апреле высокий риск простудиться, одевайтесь теплее.
♍️ Дева
Год будет богат на свежие идеи и начинания. Начните то, что давно откладывали. Интересные идеи, вопросы и решения придут в самый неожиданный момент. Сохраняйте их сразу - запишите в блокнот, голосовое сообщение, как угодно, а то улетят. Обязательно делайте бэкапы и резервные копии.
♎️ Весы
В этом году у Весов будет шанс попробовать себя в руководящей роли. Готовьтесь заранее - почитайте статьи по управлению людьми и проектами. Подтяните тайм-менеджмент, иначе времени на хобби и внерабочие активности совсем не останется.
♏️ Скорпион
В этом году вы будете на переднем фронте. Вас ждут горячие фиксы и спасение команды перед дедлайном. Будет сложно, но Сатурн вам поможет. Помните об отдыхе и набирайтесь сил в спокойное время.
♐️ Стрелец
Пересмотрите приоритеты в жизни, попробуйте смежные IT направления. Возможно позиция тимлида, менеджера или аналитика раскроют вас с новой стороны. В этом году особую важность приобретут межличностные отношения. Октябрь станет самым прибыльным месяцем в году.
♑️ Козерог
Для вас 2021 год — это борьба со своими слабостями. Уделяйте больше внимания тестированию и самопроверке. Разберитесь с NoSQL: книга для начинающих, для продолжающих. Хорошей идеей будет сходить на каток и покататься на ватрушках.
♒️ Водолей
Лучшее время для решительных шагов — начало весны. Много возможностей принесёт нетворкинг - поддерживайте тёплые отношения с коллегами, участвуйте в конференциях, митапах и корпоративных мероприятиях. Идите в ногу со временем - освойте Kotlin и Cloud computing.
♓️ Рыбы
Наступает период, когда пора применить все накопленные знания. А возможности для этого обязательно будут. Меркурий помешает сделать важные задачи в срок, поэтому закладывайте на выполнение в 2 раза больше времени. Лето подкинет массу интересных вариантов для отдыха.
Дружите со звёздами, они плохого не посоветуют💫
👍2
У аннотации Test определены все возможные Target. В какой строке будет ошибка компиляции (если будет)?
В какой строке будет ошибка компиляции (если будет)?
Anonymous Poll
16%
1
17%
2
4%
3
11%
4
35%
5
18%
Всё отлично скомпилируется
Аннотации, часть 1: обзор
Аннотации - дополнительная информация к исходному коду.
Первая часть будет о том, как сделать свою аннотацию, а во второй расскажу, когда и зачем это нужно.
Создать аннотацию легко:
Во времена java 4 аннотаций не было и для дополнительной информации классу добавляли интерфейс-маркер. В интерфейсах Cloneable, Serializable, Remote нет методов, они используются только как дополнительный признак класса.
Подход рабочий, но похож на костыль. Цель интерфейса - показать контракт класса, поэтому для маркировки кода в java 5 ввели аннотации.
Вернёмся в наши дни. Посмотрим исходный код @Deprecated:
🔸Поля
Содержат доп. информацию. Если указать значение по умолчанию, поле становится необязательным:
🔸Список внутри @Target показывает элементы, для которых работает аннотация.
В java 7 аннотации доступны для классов, методов, параметров, полей и переменных.
В java 8 аннотации действуют везде, где указан тип. Можно писать даже такое:
Аннотации нельзя ставить для имён переменных. Правильный ответ на вопрос перед постом - ошибка в 5 строке:
✅
@Target и @Retention- это мета аннотации, то есть аннотации для аннотаций.
Какие ещё бывают мета-аннотации:
🔸@Documented - аннотация появится в JavaDoc
🔸@Inherited - наследуется подклассами
🔸@Repeatable (Java 8) - можно использовать несколько раз для одного элемента. Иногда такое приятнее читать, чем один массив:
Аннотации - дополнительная информация к исходному коду.
@Override, @Deprecated, @SuppressWarnings - вот это всё.Первая часть будет о том, как сделать свою аннотацию, а во второй расскажу, когда и зачем это нужно.
Создать аннотацию легко:
public @interface MyAnnotation {}
❓Почему ключевое слово @интерфейс, а не @аннотейшн? Во времена java 4 аннотаций не было и для дополнительной информации классу добавляли интерфейс-маркер. В интерфейсах Cloneable, Serializable, Remote нет методов, они используются только как дополнительный признак класса.
Подход рабочий, но похож на костыль. Цель интерфейса - показать контракт класса, поэтому для маркировки кода в java 5 ввели аннотации.
Вернёмся в наши дни. Посмотрим исходный код @Deprecated:
@Retention(RUNTIME)На этом примере видно, из чего состоит аннотация:
@Target(value={FIELD,…})
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
🔸Поля
Содержат доп. информацию. Если указать значение по умолчанию, поле становится необязательным:
@Deprecated(since="14")// forRemoval по умолчанию false
🔸Список внутри @Target показывает элементы, для которых работает аннотация.
В java 7 аннотации доступны для классов, методов, параметров, полей и переменных.
В java 8 аннотации действуют везде, где указан тип. Можно писать даже такое:
▫️new @Test Account()
▫️throws @Test IOException
▫️implements @Test Comparable<@Test
T>
Такие аннотации называются type annotations и используются в IDE и компиляторах для анализа и строгого контроля типов.Аннотации нельзя ставить для имён переменных. Правильный ответ на вопрос перед постом - ошибка в 5 строке:
✅
@Test String doubled
❌ String @Test out
🔸@Retention определяет, когда доступна аннотация и как её можно использовать. Все виды подробно рассмотрим во второй части.@Target и @Retention- это мета аннотации, то есть аннотации для аннотаций.
Какие ещё бывают мета-аннотации:
🔸@Documented - аннотация появится в JavaDoc
🔸@Inherited - наследуется подклассами
🔸@Repeatable (Java 8) - можно использовать несколько раз для одного элемента. Иногда такое приятнее читать, чем один массив:
@Schedule(dayOfMonth="last")Создать аннотацию легко, правильно применить - уже сложнее. С этим вопросом разберёмся во второй части.
@Schedule(dayOfWeek="Fri", hour="23")
❤1
Аннотации, часть 2: как использовать
Продолжим вчерашнюю тему. Рассмотрим Retention, и когда пригодится самодельная аннотация.
@Retention определяет, на каком этапе доступна аннотация:
🔹 SOURCE - аннотация видна только во время компиляции
🔹 CLASS - доступна также в байт-коде
🔹 RUNTIME - видна всегда, даже во время работы программы
Доступность выбирается исходя из цели. Поэтому перейдём к кейсам.
Что можно сделать через аннотации?
1️⃣ Объединить уже существующие.
Самый популярный и простой случай. Если в проекте несколько аннотаций часто идут вместе, объедините их в одну. Если у всех компонентов есть обработчики, то больше ничего делать не надо, всё заработает само.
Пример: @SpringBootApplication - это комбинация @Configuration, @EnableAutoConfiguration и @ComponentScan.
2️⃣ Генерация кода и файлов.
Происходит на этапе компиляции:
🔸 Отмечаем код аннотацией
🔸 Создаём класс-наследник от AbstractProcessor. Определяем, на какие аннотации реагировать
🔸 Вытаскиваем дополнительную информацию через
🔸 Включаем процессор в компиляцию. В maven-compiler-plugin это секция annotationProcessors.
Для обработки подойдут аннотации с любой RetentionPolicy.
Что получаем:
❌ Долгая компиляция
❌ Специфичное тестирование. Пример
😐 Сложный и запутанный код
✅ Не тратится время на старте приложений
Этот подход используется в библиотеке Lombok, микрофреймворках Quarkus и Micronaut, в Android фреймворке Dagger.
Библиотека Dekorate на основе аннотаций создаёт манифесты для Kubernetes и OpenShift.
3️⃣ Статический анализ.
Алгоритм такой же - создать наследник от AbstractProcessor, добавить в процесс компиляции.
Примеры: библиотека Google Error Prone ищет в коде ошибки, Hibernate Validator проверяет, что аннотации Hibernate корректно расставлены.
4️⃣ Работа с байт-кодом.
Редкий случай. В байт-коде остаются аннотации с RetentionPolicy.CLASS или RUNTIME.
5️⃣ Создать объекты и прокси-классы.
Доступно для аннотаций с RetentionPolicy.RUNTIME.
Основа работы многих фреймворков: Spring, Hibernate, Java EE. Под работу с аннотациями в рантайме заточены многие библиотеки: Reflections, Spring. Работать с ними удобно и приятно.
Обработка рантайм-аннотаций обычно происходит на старте приложения, поэтому запуск сервиса может занимать несколько минут.
❌Теперь рассмотрим анти-кейсы, для чего аннотации НЕ нужны:
1️⃣ Отметить код для себя или команды.
Поставить аннотацию @RefactorASAP легко, но без дальнейших действий это бесполезно. Аннотации нужно обрабатывать, и делать это автоматически.
Чтобы запомнить место в коде, используйте TODO комментарии в Intellij IDEA.
2️⃣ Для бизнес-логики.
Аннотации легко добавить в обход ООП и основной логики. Так можно быстро решить проблему, но долгосрочно это неудачный вариант:
❌ Нельзя контролировать процесс целиком
❌ Сложно писать тесты
❌ Сложно дебажить
❌ Внезапные сайд-эффекты
Частая ситуация на Spring проектах: на старте запускаются десятки @PostConstruct. Если в процессе возникает ошибка, то найти и исправить её непросто.
Но вообще, чем меньше вы полагаетесь на аннотации, тем лучше.
Продолжим вчерашнюю тему. Рассмотрим Retention, и когда пригодится самодельная аннотация.
@Retention определяет, на каком этапе доступна аннотация:
🔹 SOURCE - аннотация видна только во время компиляции
🔹 CLASS - доступна также в байт-коде
🔹 RUNTIME - видна всегда, даже во время работы программы
Доступность выбирается исходя из цели. Поэтому перейдём к кейсам.
Что можно сделать через аннотации?
1️⃣ Объединить уже существующие.
Самый популярный и простой случай. Если в проекте несколько аннотаций часто идут вместе, объедините их в одну. Если у всех компонентов есть обработчики, то больше ничего делать не надо, всё заработает само.
Пример: @SpringBootApplication - это комбинация @Configuration, @EnableAutoConfiguration и @ComponentScan.
2️⃣ Генерация кода и файлов.
Происходит на этапе компиляции:
🔸 Отмечаем код аннотацией
🔸 Создаём класс-наследник от AbstractProcessor. Определяем, на какие аннотации реагировать
🔸 Вытаскиваем дополнительную информацию через
.class
Deprecated d = Account.class. getAnnotation(Deprecated.class)🔸 Делаем что-то полезное
🔸 Включаем процессор в компиляцию. В maven-compiler-plugin это секция annotationProcessors.
Для обработки подойдут аннотации с любой RetentionPolicy.
Что получаем:
❌ Долгая компиляция
❌ Специфичное тестирование. Пример
😐 Сложный и запутанный код
✅ Не тратится время на старте приложений
Этот подход используется в библиотеке Lombok, микрофреймворках Quarkus и Micronaut, в Android фреймворке Dagger.
Библиотека Dekorate на основе аннотаций создаёт манифесты для Kubernetes и OpenShift.
3️⃣ Статический анализ.
Алгоритм такой же - создать наследник от AbstractProcessor, добавить в процесс компиляции.
Примеры: библиотека Google Error Prone ищет в коде ошибки, Hibernate Validator проверяет, что аннотации Hibernate корректно расставлены.
4️⃣ Работа с байт-кодом.
Редкий случай. В байт-коде остаются аннотации с RetentionPolicy.CLASS или RUNTIME.
5️⃣ Создать объекты и прокси-классы.
Доступно для аннотаций с RetentionPolicy.RUNTIME.
Основа работы многих фреймворков: Spring, Hibernate, Java EE. Под работу с аннотациями в рантайме заточены многие библиотеки: Reflections, Spring. Работать с ними удобно и приятно.
Обработка рантайм-аннотаций обычно происходит на старте приложения, поэтому запуск сервиса может занимать несколько минут.
❌Теперь рассмотрим анти-кейсы, для чего аннотации НЕ нужны:
1️⃣ Отметить код для себя или команды.
Поставить аннотацию @RefactorASAP легко, но без дальнейших действий это бесполезно. Аннотации нужно обрабатывать, и делать это автоматически.
Чтобы запомнить место в коде, используйте TODO комментарии в Intellij IDEA.
2️⃣ Для бизнес-логики.
Аннотации легко добавить в обход ООП и основной логики. Так можно быстро решить проблему, но долгосрочно это неудачный вариант:
❌ Нельзя контролировать процесс целиком
❌ Сложно писать тесты
❌ Сложно дебажить
❌ Внезапные сайд-эффекты
Частая ситуация на Spring проектах: на старте запускаются десятки @PostConstruct. Если в процессе возникает ошибка, то найти и исправить её непросто.
Но вообще, чем меньше вы полагаетесь на аннотации, тем лучше.
👍3❤1
Stream API: новые методы в Java 16
16 марта вышла java 16. Новые фичи входят во вторую превью стадию перед главным релизом 2021 - java 17 LTS.
Существующие классы тоже развиваются. В java 16 в Stream API появилось 4 новых метода, которые мы и рассмотрим в этом посте.
1️⃣
Новый метод не совсем равнозначный:
▫️Collectors.toList() возвращает экземпляр ArrayList.
▫️Новый метод toList() возвращает неизменяемый список.
2️⃣ mapMulti
Это оптизированный flatMap. Объясню суть на примере. Заказ - класс Order, товар - класс Item. Заказ состоит из нескольких товаров. Из списка заказов хотим получить список всех товаров.
Минус: объект стрима создаётся всегда, даже для пустых списков.
mapMulti устраняет этот недостаток:
mapMulti принимает на вход (order, consumer):
▪️order - элемент стрима, в нашем случае - заказ
▪️consumer - следующий этап в стриме. Наша задача - передать этому этапу все будущие элементы. Берём у заказа товары и для каждого вызываем consumer.accept(item).
Мультимэп не знает, для каких объектов будет вызван accept, и не может вывести тип выходных элементов. Поэтому для нормальной работы тип надо указать явно:
✅ Небольшое количество элементов в списках.
Например, много заказов с 1-2 товарами
✅ Элементы легко получить без Stream API
Основная фишка mapMulti - нет промежуточных стримов. Если внутри метода создаётся стрим, то вся выгода сходит на нет.
❌ order.getItems().stream()…
Есть три вариации метода:
🔸IntStream mapMultiToInt
🔸LongStream mapMultiToLong
🔸DoubleStream mapMultiToDouble
Для них выходной тип не указывается.
16 марта вышла java 16. Новые фичи входят во вторую превью стадию перед главным релизом 2021 - java 17 LTS.
Существующие классы тоже развиваются. В java 16 в Stream API появилось 4 новых метода, которые мы и рассмотрим в этом посте.
1️⃣
toList()
С 25 по 27 ноября была серия постов о коллекторах (часть 1, часть 2, часть 3). Там я писала, что вместоcollect(Collectors.toList())было бы удобно писать просто
toList()30 ноября разработчик Oracle добавил метод toList() в класс Stream. Вряд ли он читает этот канал, но совпадение интересное🙂
Новый метод не совсем равнозначный:
▫️Collectors.toList() возвращает экземпляр ArrayList.
▫️Новый метод toList() возвращает неизменяемый список.
2️⃣ mapMulti
Это оптизированный flatMap. Объясню суть на примере. Заказ - класс Order, товар - класс Item. Заказ состоит из нескольких товаров. Из списка заказов хотим получить список всех товаров.
orders.stream()flatMap переводит "список списков" в один список. Товары для каждого заказа превращаются в Stream, а метод flatMap объединяет эти стримы в один.
.flatMap(order->order.getItems().stream())
.toList();
Минус: объект стрима создаётся всегда, даже для пустых списков.
mapMulti устраняет этот недостаток:
orders.stream()Что происходит:
.<Item>mapMulti((order, consumer) ->
order.getItems().forEach(item -> consumer.accept(item))
).toList();
mapMulti принимает на вход (order, consumer):
▪️order - элемент стрима, в нашем случае - заказ
▪️consumer - следующий этап в стриме. Наша задача - передать этому этапу все будущие элементы. Берём у заказа товары и для каждого вызываем consumer.accept(item).
Мультимэп не знает, для каких объектов будет вызван accept, и не может вывести тип выходных элементов. Поэтому для нормальной работы тип надо указать явно:
<Item>mapMulti(…)Когда использовать mapMulti?
✅ Небольшое количество элементов в списках.
Например, много заказов с 1-2 товарами
✅ Элементы легко получить без Stream API
Основная фишка mapMulti - нет промежуточных стримов. Если внутри метода создаётся стрим, то вся выгода сходит на нет.
❌ order.getItems().stream()…
Есть три вариации метода:
🔸IntStream mapMultiToInt
🔸LongStream mapMultiToLong
🔸DoubleStream mapMultiToDouble
Для них выходной тип не указывается.
❤1
Intellij IDEA: как выучить шорткаты
В IDEA сотни горячих клавиш. С ними удобно работать, не надо тащить курсор через 2 монитора и бродить по контекстному меню. Есть только одна проблема - шорткаты сложно запомнить сразу.
Видела, как люди распечатывают огромный список горячих клавиш и вешают рядом с монитором. Поделюсь более прогрессивными методами:
1️⃣ Выучить топ-15
Статистика использования IDE находится в Help → Productivity Guide. Сортируем по колонке Used, получаем часто используемые команды. В описании указаны шорткаты. Запоминаем горячие клавиши для 10-15 действий, и продуктивность заметно растёт.
2️⃣ Плагин Key Promoter X
Установка: File → Settings → Plugins → Key Promoter X.
При действиях с мышкой в углу всплывает подсказка-шорткат.
Здесь работает правило 80/20: шорткаты для навигации, поиска и рефакторинга покрывают большинство ежедневных задач. Их легко выучить с помощью этих двух инструментов.
В IDEA сотни горячих клавиш. С ними удобно работать, не надо тащить курсор через 2 монитора и бродить по контекстному меню. Есть только одна проблема - шорткаты сложно запомнить сразу.
Видела, как люди распечатывают огромный список горячих клавиш и вешают рядом с монитором. Поделюсь более прогрессивными методами:
1️⃣ Выучить топ-15
Статистика использования IDE находится в Help → Productivity Guide. Сортируем по колонке Used, получаем часто используемые команды. В описании указаны шорткаты. Запоминаем горячие клавиши для 10-15 действий, и продуктивность заметно растёт.
2️⃣ Плагин Key Promoter X
Установка: File → Settings → Plugins → Key Promoter X.
При действиях с мышкой в углу всплывает подсказка-шорткат.
Здесь работает правило 80/20: шорткаты для навигации, поиска и рефакторинга покрывают большинство ежедневных задач. Их легко выучить с помощью этих двух инструментов.
Intellij IDEA: Database View
Продолжая тему с БД, расскажу как можно работать с базой через Intellij IDEA.
Окошко открывается так: View → Tool Windows → Database
Подключить базу просто, самые необходимые функции есть:
▫️ Информация о таблицах, столбцах, индексах и т.д
▫️ Выполнить запрос
▫️ Выгрузить данные или метадату
Какие базы доступны:
✅ Реляционные: Oracle, PostgreSQL, MySQL
✅ Многие NoSQL: Mongo, Cassandra, Hive, ClickHouse, Vertica
✅ Экзотичные: Exasol, Greenplum и Snowflake
❌ Redis, Couchbase, HBase, CouchDB, Neo4j
Полный список баз на сегодня:
Продолжая тему с БД, расскажу как можно работать с базой через Intellij IDEA.
Окошко открывается так: View → Tool Windows → Database
Подключить базу просто, самые необходимые функции есть:
▫️ Информация о таблицах, столбцах, индексах и т.д
▫️ Выполнить запрос
▫️ Выгрузить данные или метадату
Какие базы доступны:
✅ Реляционные: Oracle, PostgreSQL, MySQL
✅ Многие NoSQL: Mongo, Cassandra, Hive, ClickHouse, Vertica
✅ Экзотичные: Exasol, Greenplum и Snowflake
❌ Redis, Couchbase, HBase, CouchDB, Neo4j
Полный список баз на сегодня:
Лучшие посты за 3 месяца
Если что-то пропустили, рекомендую прочитать:
Java Core:
Исключения: checked и unchecked
Default методы: неудачный кейс
Будущее java: ближайшие 5-7 лет
Лонгрид про сборщики мусора
Лонгрид про коллекторы в Stream API
Intellij IDEA:
Как быстро редактировать код
Прочее:
Как найти работу без HeadHunter
Spring: статистика использования
Cпасибо, что читаете, ставите лайки и даёте обратную связь❤️
Если что-то пропустили, рекомендую прочитать:
Java Core:
Исключения: checked и unchecked
Default методы: неудачный кейс
Будущее java: ближайшие 5-7 лет
Лонгрид про сборщики мусора
Лонгрид про коллекторы в Stream API
Intellij IDEA:
Как быстро редактировать код
Прочее:
Как найти работу без HeadHunter
Spring: статистика использования
Cпасибо, что читаете, ставите лайки и даёте обратную связь❤️
В прошлый вторник вышла java 16, последний пробный шар перед главным релизом года - java 17. Чего-то совсем нового там нет, поэтому на этой неделе поговорим о сериализации. Лонгрид будет полезен начинающим разработчикам.
⭐️ Часть 1: что такое сериализация, зачем она нужна, как сериализуются и десериализуются объекты
⭐️ Часть 2: serialVersionUID и проблемы сериализации в Java
⭐️ Часть 3: сериализация на практике
⭐️ Часть 1: что такое сериализация, зачем она нужна, как сериализуются и десериализуются объекты
⭐️ Часть 2: serialVersionUID и проблемы сериализации в Java
⭐️ Часть 3: сериализация на практике
Вопрос
Есть класс Parent с полем parentValue. У него есть наследник - класс Child с полем childValue. Класс Child помечен интерфейсом Serializable
Какими будут поля экземпляра Child после десереализации?
(щёлкните на картинку, чтобы открыть код полностью)
Есть класс Parent с полем parentValue. У него есть наследник - класс Child с полем childValue. Класс Child помечен интерфейсом Serializable
Какими будут поля экземпляра Child после десереализации?
(щёлкните на картинку, чтобы открыть код полностью)
👍2
Какими будут поля экземпляра Child после десереализации?
Anonymous Poll
10%
parentValue: 1 childValue: 50
16%
parentValue: 2 childValue: 50
19%
parentValue: 3 childValue: 55
52%
parentValue: 4 childValue: 50
4%
parentValue: 4 childValue: 55
Сериализация, часть 1: обзор
Сервисы редко существуют сами по себе, они активно обмениваются данными с окружающим миром и другими сервисами.
Сериализация превращает Java объект в набор байтов, который можно передать по сети или куда-нибудь записать. Также встречается под именами marshalling или encoding.
Десериализация восстанавливает Java объект из полученных байтов. Где-то этот процесс называется unmarshalling или decoding.
Пожелания:
1️⃣ Чтобы набор байтов занимал поменьше места. Чем короче сообщение, тем быстрее оно передаётся
2️⃣Минимум усилий со стороны программиста
Сериализация появилась в первой версии Java, и по сравнению с другими языками это была фантастика. JVM брала большую часть работы на себя. Байтовые сообщения получались компактными и быстро стали частью EJB, JMX, JMS и т.д
С тех пор механизм сериализации в java не менялся. Классы, экземпляры которых покидают JVM, должны реализовать интерфейс Serializable:
Интерфейс Externalizable даёт полный контроль над итоговым набором байтов. Хотите записать java объект в PDF или зашифровать данные - реализуйте методы Externalizable.
Как происходит сериализация Serializable классов:
1️⃣ Проверка полей
static и transient поля не участвуют в сериализации. Остальные поля должны быть либо Serializable, либо примитивами. Иначе разработчик получит NotSerializableException.
2️⃣ Объект превращается в байты
Для передачи данных обычно используется ObjectOutputStream, но часто он скрыт за фреймворком или библиотекой. Что туда пишет JVM:
🔸 Поля-заголовки
🔸 Информация о классе:
▪️ Имя класса
▪️ serialVersionUID
▪️ Количество полей
▪️ Информация по каждому полю:
▫️ Тип (имя класса или примитив)
▫️ Длина
▫️ Имя переменной
🔸 Информация про Serializable родительские классы в таком же формате
🔸 Значения переменных Serializable родительских классов
🔸 Значения переменных текущего класса. Если переменная - не примитив, то схема повторяется - записывается информация про класс и значения полей.
В примере перед постом класс Parent не реализует Serializable, поэтому parentValue не записывается в итоговый стрим, только childValue.
3️⃣ Набор байтов готов, можно отправлять.
Десериализация по шагам
Посмотрим на примере класса Parent и Child из примера выше.
1️⃣ Читаем из полученных байтов информацию о классе и о всех ближайших Serializable родителях.
2️⃣ Ищем ближайший НЕ Serializable родитель. В примере это класс Parent
3️⃣ Вызываем у класса Parent конструктор без параметров.
Тут проставляется parentValue = 2
4️⃣ Получаем экземпляр. Конструктор Child не используется, остальные поля проставляются внутренними механизмами JVM.
5️⃣ Чтение полей из потока байтов. В нашем примере передано только childValue. Записываем: childValue = 50;
Итого: в консоль выведется 2 и 50. Хотя изначально мы создавали объект с parentValue = 4, это поле не передаётся при сериализации, поэтому используется значение из конструктора Parent().
Как исправить ситуацию? Есть два варианта:
💊 Добавить классу Parent интерфейс Serializable
💊 Переопределить в классе Child методы writeObject и readObject. Они не определены в Serializable, но JVM найдёт их в процессе сериализации.
В writeObject задаётся, какие поля и в каком порядке запишутся в итоговый объект:
В следующем посте поговорим, зачем в сообщении нужен serialVersionUID, когда его задавать напрямую и менять, а также про недостатки Java сериализации.
Сервисы редко существуют сами по себе, они активно обмениваются данными с окружающим миром и другими сервисами.
Сериализация превращает Java объект в набор байтов, который можно передать по сети или куда-нибудь записать. Также встречается под именами marshalling или encoding.
Десериализация восстанавливает Java объект из полученных байтов. Где-то этот процесс называется unmarshalling или decoding.
Пожелания:
1️⃣ Чтобы набор байтов занимал поменьше места. Чем короче сообщение, тем быстрее оно передаётся
2️⃣Минимум усилий со стороны программиста
Сериализация появилась в первой версии Java, и по сравнению с другими языками это была фантастика. JVM брала большую часть работы на себя. Байтовые сообщения получались компактными и быстро стали частью EJB, JMX, JMS и т.д
С тех пор механизм сериализации в java не менялся. Классы, экземпляры которых покидают JVM, должны реализовать интерфейс Serializable:
class UserRequest implements SerializableУ него нет обязательных методов, это интерфейс-маркер.
Интерфейс Externalizable даёт полный контроль над итоговым набором байтов. Хотите записать java объект в PDF или зашифровать данные - реализуйте методы Externalizable.
Как происходит сериализация Serializable классов:
1️⃣ Проверка полей
static и transient поля не участвуют в сериализации. Остальные поля должны быть либо Serializable, либо примитивами. Иначе разработчик получит NotSerializableException.
2️⃣ Объект превращается в байты
Для передачи данных обычно используется ObjectOutputStream, но часто он скрыт за фреймворком или библиотекой. Что туда пишет JVM:
🔸 Поля-заголовки
🔸 Информация о классе:
▪️ Имя класса
▪️ serialVersionUID
▪️ Количество полей
▪️ Информация по каждому полю:
▫️ Тип (имя класса или примитив)
▫️ Длина
▫️ Имя переменной
🔸 Информация про Serializable родительские классы в таком же формате
🔸 Значения переменных Serializable родительских классов
🔸 Значения переменных текущего класса. Если переменная - не примитив, то схема повторяется - записывается информация про класс и значения полей.
В примере перед постом класс Parent не реализует Serializable, поэтому parentValue не записывается в итоговый стрим, только childValue.
3️⃣ Набор байтов готов, можно отправлять.
Десериализация по шагам
Посмотрим на примере класса Parent и Child из примера выше.
1️⃣ Читаем из полученных байтов информацию о классе и о всех ближайших Serializable родителях.
2️⃣ Ищем ближайший НЕ Serializable родитель. В примере это класс Parent
3️⃣ Вызываем у класса Parent конструктор без параметров.
Тут проставляется parentValue = 2
4️⃣ Получаем экземпляр. Конструктор Child не используется, остальные поля проставляются внутренними механизмами JVM.
5️⃣ Чтение полей из потока байтов. В нашем примере передано только childValue. Записываем: childValue = 50;
Итого: в консоль выведется 2 и 50. Хотя изначально мы создавали объект с parentValue = 4, это поле не передаётся при сериализации, поэтому используется значение из конструктора Parent().
Как исправить ситуацию? Есть два варианта:
💊 Добавить классу Parent интерфейс Serializable
💊 Переопределить в классе Child методы writeObject и readObject. Они не определены в Serializable, но JVM найдёт их в процессе сериализации.
В writeObject задаётся, какие поля и в каком порядке запишутся в итоговый объект:
private void writeObject(…out) {
out.writeInt(parentValue);
out.writeInt(childValue);
}
В readObject указывается, какие поля и в каком порядке читать из байтового стрима:private void readObject(…in){
int parentValue=in.readInt();
setParentValue(parentValue);
this.childValue=in.readInt();
}
В любом из вариантов десериализованный объект напечатает 4 и 50.В следующем посте поговорим, зачем в сообщении нужен serialVersionUID, когда его задавать напрямую и менять, а также про недостатки Java сериализации.
👍4
Что означает поле serialVersionUID в Serializable классах?
Anonymous Poll
24%
Версия класса. Первое значение должно быть 1L, дальше увеличивается на единицу при изменении класса
33%
Идентификатор класса. Должен быть уникальным в рамках системы
12%
Быстрая проверка, можно ли начинать десериализацию. Может быть любым
11%
Хэш экземпляра. Помогает кэшировать объекты при сериализации. Не должен меняться
20%
Сейчас ничего, но в ранних версиях Java было обязательным требованием для Serializable классов
Сериализация, часть 2: serialVersionUID
При использовании сериализации в класс рекомендуют добавить такое поле:
Итак, в процессе сериализации 2 участника: отправитель и получатель. У каждого из них есть код класса Х. Отправитель сериализует экземпляр Х и отправляет по сети.
Из прошлого поста вы знаете, что в этом массиве байтов есть serialVersionUID. Получатель читает имя класса и первым делом сравнивает serialVersionUID из сообщения с serialVersionUID своего класса.
▫️ Если совпадают - начинается десериализация
▫️ Если нет - выбрасывается
Другой вариант - когда класс эволюционирует и передаёт другой набор данных. Сервисы не всегда обновляются одновременно, поэтому в переходный период возникают две проблемы:
🔸 Как новому коду читать данные, созданные старым кодом? (backward compatibility)
🔸 Как старому коду читать данные, созданные новым кодом? (forward compatibility)
Приходится мириться с наличием старых версий и писать код соответственно:
1️⃣ Задать в классе serialVersionUID. Никогда не менять
2️⃣ Добавить методы readObject и writeObject и прописать порядок записи и чтения полей
3️⃣ Писать тесты на совместимость версий
Набор изменений при этом весьма ограничен:
✅ Можно добавлять новые поля в конец байтового стрима
✅ Можно менять видимость полей и методов
❌ Нельзя удалять поля
❌ Нельзя менять тип полей
Пара "имя класса-serialVersionUID" работает как фильтр - можно десериализовать набор байтов или нет. Когда serialVersionUID не задан в классе, он генерируется JVM. Для прямой и обратной совместимости serialVersionUID может быть любым, но постоянным. Методы writeObject и readObject задают чёткий порядок чтения/записи, но сильно разгуляться не получится.
Уже отсюда понятно, что на практике с сериализацией море проблем:
▪️ Разработка усложняется: всегда нужно иметь в виду forward/backward совместимость, набор доступных изменений сильно ограничен.
▪️ Нарушается инкапсуляция, так как private поля передаются по сети.
▪️ Ограниченные сценарии использования. Получатель и отправитель должны быть на java.
▪️ Небезопасно. Десериализация - сладкий пирожок для разных типов атак. В 2016 их было так много, что тот год на конференциях называли Java deserialization apocalypse year. В 2021 году уязвимости на основе сериализации встречаются даже в Intellij IDEA и Kubernetes.
Что с этим делать и как сериализация выглядит на практике - поговорим в третьей части.
При использовании сериализации в класс рекомендуют добавить такое поле:
private static final long serialVersionUID = 27507467L;В этом посте разберёмся, зачем это нужно, когда прописывать serialVersionUID и когда менять. В конце поговорим про недостатки сериализации в java.
Итак, в процессе сериализации 2 участника: отправитель и получатель. У каждого из них есть код класса Х. Отправитель сериализует экземпляр Х и отправляет по сети.
Из прошлого поста вы знаете, что в этом массиве байтов есть serialVersionUID. Получатель читает имя класса и первым делом сравнивает serialVersionUID из сообщения с serialVersionUID своего класса.
▫️ Если совпадают - начинается десериализация
▫️ Если нет - выбрасывается
InvalidClassException
Когда serialVersionUID не указан в классе явно, JVM вычисляет его в рантайме на основе имени класса, интерфейсов, полей и методов. Добавили новый метод - serialVersionUID изменился. Поэтому рекомендуется зафиксировать serialVersionUID, даже если поля класса не меняются.Другой вариант - когда класс эволюционирует и передаёт другой набор данных. Сервисы не всегда обновляются одновременно, поэтому в переходный период возникают две проблемы:
🔸 Как новому коду читать данные, созданные старым кодом? (backward compatibility)
🔸 Как старому коду читать данные, созданные новым кодом? (forward compatibility)
Приходится мириться с наличием старых версий и писать код соответственно:
1️⃣ Задать в классе serialVersionUID. Никогда не менять
2️⃣ Добавить методы readObject и writeObject и прописать порядок записи и чтения полей
3️⃣ Писать тесты на совместимость версий
Набор изменений при этом весьма ограничен:
✅ Можно добавлять новые поля в конец байтового стрима
✅ Можно менять видимость полей и методов
❌ Нельзя удалять поля
❌ Нельзя менять тип полей
Пара "имя класса-serialVersionUID" работает как фильтр - можно десериализовать набор байтов или нет. Когда serialVersionUID не задан в классе, он генерируется JVM. Для прямой и обратной совместимости serialVersionUID может быть любым, но постоянным. Методы writeObject и readObject задают чёткий порядок чтения/записи, но сильно разгуляться не получится.
Уже отсюда понятно, что на практике с сериализацией море проблем:
▪️ Разработка усложняется: всегда нужно иметь в виду forward/backward совместимость, набор доступных изменений сильно ограничен.
▪️ Нарушается инкапсуляция, так как private поля передаются по сети.
▪️ Ограниченные сценарии использования. Получатель и отправитель должны быть на java.
▪️ Небезопасно. Десериализация - сладкий пирожок для разных типов атак. В 2016 их было так много, что тот год на конференциях называли Java deserialization apocalypse year. В 2021 году уязвимости на основе сериализации встречаются даже в Intellij IDEA и Kubernetes.
Что с этим делать и как сериализация выглядит на практике - поговорим в третьей части.
👍3
Сериализация, часть 3: практика
На моей первой работе в проекте активно использовалась JVM сериализация. Java объекты передавались между сервисами и записывались в БД. Было сложно, зато теперь я вижу проблемы совместимости на раз-два. В прошлом посте поговорили о недостатках JVM сериализации. В этом посте обсудим, как их избежать.
Что говорят авторитетные источники:
Effective Java: нет никаких причин использовать java сериализацию в новых системах.
Brian Goetz, архитектор Java: когда меня спрашивают, о какой фиче я сожалею больше всего, то ответ прост - сериализация.
Java cериализация была отличным решением для условий 1996 года:
▫️ Медленный интернет
▫️ Небольшая память
▫️ Скудный набор языков и платформ
Сегодня это уже не актуально, и в 2021 ситуация такая:
▪️ Данные быстро меняются
▪️ Данных много
▪️ В системе одновременно существуют несколько версий данных
▪️ Быстрый интернет
▪️ Сервисы пишутся на разных языках и платформах
Данные передаются по сети в виде байтов, но используется стандартная структура сообщения, а не та, которую задаёт JVM. Такой подход снимает 90% проблем сериализации. Популярные форматы делятся на две группы:
1️⃣ Текстовые: JSON, CSV, XML
✅ Легко читаются человеком
✅ Библиотеки для любых платформ и языков
❌ Избыточность. В JSON и XML названия полей занимают половину сообщения, а в CSV миллион запятых
❌ Плохая поддержка типов. Поля классов, большие числа, даты - всё передаётся как строки
2️⃣ Бинарные
Данные пишутся максимально компактно. К этой группе относятся protobuf, Thrift, Avro, Parquet. Для каждого типа сообщений создаётся схема. В ней перечисляются поля, их размер, иногда порядковый номер. Для protobuf схема выглядит так:
✅ Короткие сообщения. Вместо имён полей используются порядковые номера(protobuf, Thrift), либо данные просто идут подряд(Avro, Parquet).
Schema Registry
И для текстовых, и для бинарных форматов остаётся проблема прямой и обратной совместимости. Менять схему можно, но в ограниченных пределах. Если формат данных меняется часто, то поможет паттерн Schema Registry.
Это отдельный компонент, который хранит все версии схем данных и сопутствующую информацию:
🔹 ID схемы: id = 15
🔹 Название: subject = "orderRequest"
🔹 Версия: version = 3
🔹 Сама схема: schema = …
Отправитель формирует сообщение и передаёт его вместе с ID схемы. Получатель берёт схему из Schema registry и читает данные. 100% совместимости это не гарантирует, но заметно упрощает работу.
Резюме:
Сериализация в java была отличным решением в своё время. Я не стала подробно описывать методы и лучшие практики Serializable/Externalizable, т.к такая сериализация осталась только в дремучих легаси проектах. Даже на собеседованиях её редко спрашивают.
Сейчас чаще используются форматы, не привязанные к конкретному языку и платформе. Но проблемы совместимости не исчезают:
🔸 Backward compatibility: чтение старых данных на новых серверах
🔸 Forward compatibility: чтение новых данных на старых серверах
Эти проблемы решаются двумя способами:
🔹 Адаптировать лучшие практики из Serializable. Подход рабочий, но набор доступных изменений сильно ограничен.
🔹 Использовать схемы данных. Они доступны для JSON, XML, SOAP, protobuf, Avro и т.д. Для упрощения работы со схемами поможет паттерн Schema Registry.
На моей первой работе в проекте активно использовалась JVM сериализация. Java объекты передавались между сервисами и записывались в БД. Было сложно, зато теперь я вижу проблемы совместимости на раз-два. В прошлом посте поговорили о недостатках JVM сериализации. В этом посте обсудим, как их избежать.
Что говорят авторитетные источники:
Effective Java: нет никаких причин использовать java сериализацию в новых системах.
Brian Goetz, архитектор Java: когда меня спрашивают, о какой фиче я сожалею больше всего, то ответ прост - сериализация.
Java cериализация была отличным решением для условий 1996 года:
▫️ Медленный интернет
▫️ Небольшая память
▫️ Скудный набор языков и платформ
Сегодня это уже не актуально, и в 2021 ситуация такая:
▪️ Данные быстро меняются
▪️ Данных много
▪️ В системе одновременно существуют несколько версий данных
▪️ Быстрый интернет
▪️ Сервисы пишутся на разных языках и платформах
Данные передаются по сети в виде байтов, но используется стандартная структура сообщения, а не та, которую задаёт JVM. Такой подход снимает 90% проблем сериализации. Популярные форматы делятся на две группы:
1️⃣ Текстовые: JSON, CSV, XML
✅ Легко читаются человеком
✅ Библиотеки для любых платформ и языков
❌ Избыточность. В JSON и XML названия полей занимают половину сообщения, а в CSV миллион запятых
❌ Плохая поддержка типов. Поля классов, большие числа, даты - всё передаётся как строки
2️⃣ Бинарные
Данные пишутся максимально компактно. К этой группе относятся protobuf, Thrift, Avro, Parquet. Для каждого типа сообщений создаётся схема. В ней перечисляются поля, их размер, иногда порядковый номер. Для protobuf схема выглядит так:
message OrderRequest {
required int64 user_id = 1;
optional string address= 2;
repeated int64 item_id = 3;
}
Отправитель создаёт массив байтов опираясь на эту схему. Получатель считает данные по той же схеме.✅ Короткие сообщения. Вместо имён полей используются порядковые номера(protobuf, Thrift), либо данные просто идут подряд(Avro, Parquet).
Schema Registry
И для текстовых, и для бинарных форматов остаётся проблема прямой и обратной совместимости. Менять схему можно, но в ограниченных пределах. Если формат данных меняется часто, то поможет паттерн Schema Registry.
Это отдельный компонент, который хранит все версии схем данных и сопутствующую информацию:
🔹 ID схемы: id = 15
🔹 Название: subject = "orderRequest"
🔹 Версия: version = 3
🔹 Сама схема: schema = …
Отправитель формирует сообщение и передаёт его вместе с ID схемы. Получатель берёт схему из Schema registry и читает данные. 100% совместимости это не гарантирует, но заметно упрощает работу.
Резюме:
Сериализация в java была отличным решением в своё время. Я не стала подробно описывать методы и лучшие практики Serializable/Externalizable, т.к такая сериализация осталась только в дремучих легаси проектах. Даже на собеседованиях её редко спрашивают.
Сейчас чаще используются форматы, не привязанные к конкретному языку и платформе. Но проблемы совместимости не исчезают:
🔸 Backward compatibility: чтение старых данных на новых серверах
🔸 Forward compatibility: чтение новых данных на старых серверах
Эти проблемы решаются двумя способами:
🔹 Адаптировать лучшие практики из Serializable. Подход рабочий, но набор доступных изменений сильно ограничен.
🔹 Использовать схемы данных. Они доступны для JSON, XML, SOAP, protobuf, Avro и т.д. Для упрощения работы со схемами поможет паттерн Schema Registry.
👍7
Phaser: конференции и реальная жизнь
На конференции Joker 2020 было 3 доклада по теме многопоточности. Один из них - Thread Safety with Phaser, StampedLock and VarHandle. На примере
Паттерн Барьер помогает координировать потоки. Он блокирует один или несколько потоков, пока не наступит какое-то событие. JDK предлагает три реализации:
1️⃣
🔸 Несколько сценариев работы
🔸 Методы мониторинга
🔸 Можно строить иерархичные структуры из нескольких
Класс
Часто автор статьи или доклада держит фокус на инструменте:
Рассказываю про Phaser → Подбираю пример
На практике последовательность другая:
Вижу проблему → Ищу варианты → Выбираю подходящий
В такой цепочке у
Зачем тогда о нём говорить?
Чётко понимать, почему что-то НЕ работает, так же полезно, как и знать лучшие практики. Причиной может быть:
🔹 Плохой дизайн и неудобные методы
🔹 Неудачная реализация и проблемы производительности
🔹 Более подходящие инструменты
Чем плох
Все реализации паттерна Барьер блокируют потоки, поэтому редко используются в нагруженных системах. Есть всего пара ситуаций, когда барьер - лучшее решение, но для их реализации достаточно
На конференции Joker 2020 было 3 доклада по теме многопоточности. Один из них - Thread Safety with Phaser, StampedLock and VarHandle. На примере
Phaser хорошо виден разрыв между теорией и практикой. В этом посте расскажу, почему.Паттерн Барьер помогает координировать потоки. Он блокирует один или несколько потоков, пока не наступит какое-то событие. JDK предлагает три реализации:
1️⃣
CountDownLatch
2️⃣ CyclicBarrier
3️⃣ Phaser
Последний - самый продвинутый:🔸 Несколько сценариев работы
🔸 Методы мониторинга
🔸 Можно строить иерархичные структуры из нескольких
Phaser
🔸 Иногда работает быстрее, чем CountDownLatch и CyclicBarrier
🔸 Обработка исключенийКласс
Phaser часто встречается на воркшопах и advanced java курсах. Можно долго рассказывать про методы, рисовать схемы и многопоточно перемножать двумерные массивы.Часто автор статьи или доклада держит фокус на инструменте:
Рассказываю про Phaser → Подбираю пример
На практике последовательность другая:
Вижу проблему → Ищу варианты → Выбираю подходящий
В такой цепочке у
Phaser нет шансов. За пределами конференций и статей этот класс не используется. Зачем тогда о нём говорить?
Чётко понимать, почему что-то НЕ работает, так же полезно, как и знать лучшие практики. Причиной может быть:
🔹 Плохой дизайн и неудобные методы
🔹 Неудачная реализация и проблемы производительности
🔹 Более подходящие инструменты
Чем плох
Phaser?Все реализации паттерна Барьер блокируют потоки, поэтому редко используются в нагруженных системах. Есть всего пара ситуаций, когда барьер - лучшее решение, но для их реализации достаточно
CountDownLatch или CyclicBarrier. Phaser неплохо спроектирован, но слишком оторван от практических задач, это наглядный пример over engineering.👍3
Как развиваться и строить свою карьеру
Чего хотят разработчики? Получать удовольствие от работы и много денег. Знать, что востребован на рынке и легко найдёшь другую работу.
Свой опыт я считаю вполне успешным - стала сеньором через 2.5 года, а ведущим - через 4. Есть закрытая ипотека, потрёпанный загран и породистый пёс. На работе любят и ценят. Уже хочу не только зарабатывать деньги, но и делиться знаниями.
Когда младшие коллеги спрашивают, что им изучать и на чём фокусироваться, я отвечаю плюс-минус одно и то же. Здесь не будет списка конкретных технологий, но есть не менее важные вещи.
Советы джуниору
Ты устроился на первую работу. Молодец, до этого этапа не доходят большинство начинающих!
Твоя задача - влиться в текущий проект и адаптироваться к его технологиям. Даже если это чудовищное легаси.
🔸 Закрывай пробелы по java core
🔸 Учись писать тесты, работать с CI и взаимодействовать с командой
🔸 Повторяй за другими, пиши код по аналогии
🔸 Если что-то непонятно - сначала поищи ответ пару часов, если не нашёл - спроси
🔸 Записывай ответы, не спрашивай одно и то же
Сейчас сложно, потом будет легче!
Советы мидлу
Сейчас самое приятное время для работы и обучения. Ты хорошо справляешься с задачами проекта. Ответственности пока мало, и рядом всегда есть старшие товарищи. Зарплата выше среднего.
Но не задирай нос. Скорее всего большинство задач ты решаешь по аналогии с теми, что уже были. Чтобы развиваться, задай себе ряд вопросов:
❓ Что будет, если я останусь на этом месте ещё год? А два?
❓ Есть ли у меня пробелы в базе: java core, concurrency, паттерны GoF, SQL?
❓ Что происходит на рынке? Актуальны ли мои знания?
Твоя задача: обрести фундамент, чтобы подстроиться к большинству проектов.
Советы сеньору
У тебя есть опыт в определённых технологиях. Ты принимаешь много решений самостоятельно и несёшь за них ответственность. Здесь чётких рекомендаций нет, но пора подумать над вопросами:
❓ Есть ли какие-то базовые знания, в которых я не уверен?
На этом этапе критически важно устранить все пробелы!
❓ Что мне интересно? На каком проекте я хочу работать? Какие задачи решать? С какими людьми работать?
❓ Что мне нужно, чтобы попасть на желаемый проект и комфортно там работать?
Твоя задача - не суета и страдания, а получать много денег и удовольствие от работы. Одновременно.
Чего хотят разработчики? Получать удовольствие от работы и много денег. Знать, что востребован на рынке и легко найдёшь другую работу.
Свой опыт я считаю вполне успешным - стала сеньором через 2.5 года, а ведущим - через 4. Есть закрытая ипотека, потрёпанный загран и породистый пёс. На работе любят и ценят. Уже хочу не только зарабатывать деньги, но и делиться знаниями.
Когда младшие коллеги спрашивают, что им изучать и на чём фокусироваться, я отвечаю плюс-минус одно и то же. Здесь не будет списка конкретных технологий, но есть не менее важные вещи.
Советы джуниору
Ты устроился на первую работу. Молодец, до этого этапа не доходят большинство начинающих!
Твоя задача - влиться в текущий проект и адаптироваться к его технологиям. Даже если это чудовищное легаси.
🔸 Закрывай пробелы по java core
🔸 Учись писать тесты, работать с CI и взаимодействовать с командой
🔸 Повторяй за другими, пиши код по аналогии
🔸 Если что-то непонятно - сначала поищи ответ пару часов, если не нашёл - спроси
🔸 Записывай ответы, не спрашивай одно и то же
Сейчас сложно, потом будет легче!
Советы мидлу
Сейчас самое приятное время для работы и обучения. Ты хорошо справляешься с задачами проекта. Ответственности пока мало, и рядом всегда есть старшие товарищи. Зарплата выше среднего.
Но не задирай нос. Скорее всего большинство задач ты решаешь по аналогии с теми, что уже были. Чтобы развиваться, задай себе ряд вопросов:
❓ Что будет, если я останусь на этом месте ещё год? А два?
❓ Есть ли у меня пробелы в базе: java core, concurrency, паттерны GoF, SQL?
❓ Что происходит на рынке? Актуальны ли мои знания?
Твоя задача: обрести фундамент, чтобы подстроиться к большинству проектов.
Советы сеньору
У тебя есть опыт в определённых технологиях. Ты принимаешь много решений самостоятельно и несёшь за них ответственность. Здесь чётких рекомендаций нет, но пора подумать над вопросами:
❓ Есть ли какие-то базовые знания, в которых я не уверен?
На этом этапе критически важно устранить все пробелы!
❓ Что мне интересно? На каком проекте я хочу работать? Какие задачи решать? С какими людьми работать?
❓ Что мне нужно, чтобы попасть на желаемый проект и комфортно там работать?
Твоя задача - не суета и страдания, а получать много денег и удовольствие от работы. Одновременно.
❤1