Сегодня начнётся онлайн конференция JLove❤️
Бесплатная!
Расписание уже готово, не забудьте подставить свой часовой пояс.
С удовольствием рассказываю о конфе второй раз: классные эксперты и получасовые доклады на любой вкус. Плюс общение со спикерами в чате, группы по интересам, афтепати, призы и подарки.
Зарегистрироваться: jlove.konfy.care
Бесплатная!
Расписание уже готово, не забудьте подставить свой часовой пояс.
С удовольствием рассказываю о конфе второй раз: классные эксперты и получасовые доклады на любой вкус. Плюс общение со спикерами в чате, группы по интересам, афтепати, призы и подарки.
Зарегистрироваться: jlove.konfy.care
Паттерн проектирования, который в соответствии с принципом единственной обязанности передает другому объекту ответственность построения требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму
Anonymous Poll
40%
Dependency injection
9%
Dependency invertion
36%
Inversion of Control
16%
Factory Method
👍2
DI vs DI vs IoC
Знаете, почему сложно внедрять всякие принципы и лучшие практики? Они рождаются из конкретных ситуаций и решают конкретные проблемы. Чтобы передать эти ценнейшие знания, ситуацию приходится абстрагировать и в итоге получается набор терминов. Что с ними делать - непонятно, слишком абстрактно.
Сегодня разберём разницу между Dependency injection, Dependency invertion и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре. Плюс поймёте, как хорошо вы программируете и какие проблемы решаете, даже не задумываясь.
Будем разбираться на простом примере.
Точка А: Сервис Service записывает логи в файл с помощью класса FileLogger:
1️⃣ Dependency injection
это когда компоненты создаются не внутри класса, а передаются в конструкторах или сеттерах. Перенесём инициализацию логгера в конструктор:
2️⃣ Dependency invertion
Буква D в аббревиатуре SOLID, формулировка состоит из двух частей:
▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции.
Суть: пусть сервис работает не с конкретным логгером, а с интерфейсом
✅ Реализацию легко заменить
✅ Оба класса проще тестировать
Почему используется слово "абстракция"? Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс самый подходящий вариант.
3️⃣ IoC - Inversion of Control
В маленьких программах жизнь начинается в методе main(). Программист создаёт объекты, вызывает методы, все шаги явно прописаны.
Inversion of Control - это когда ход выполнения программы задаёт фреймворк. Spring смотрит на классы и аннотации, а затем создаёт объекты, связывает их вместе и не даёт программе завершиться.
✅ Низкая связность - код легко менять, тестировать и переиспользовать
Spring создаёт обёртки классов и работает через Dependency Injection. Можно и по-другому: через паттерн фабричный метод, стратегия или сервис локатор.
⚔️Историческая справка.
Сервис локатор иногда встречается в легаси проектах. Это когда компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы.
🔸Dependency injection - класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
🔸Dependency invertion - класс работает с другими компонентами через интерфейс
🔸Inversion of Control - ход программы задаёт фреймворк. Соединять компоненты может Dependency injection, фабричный метод, стратегия или сервис локатор.
❗️Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
#теория
Знаете, почему сложно внедрять всякие принципы и лучшие практики? Они рождаются из конкретных ситуаций и решают конкретные проблемы. Чтобы передать эти ценнейшие знания, ситуацию приходится абстрагировать и в итоге получается набор терминов. Что с ними делать - непонятно, слишком абстрактно.
Сегодня разберём разницу между Dependency injection, Dependency invertion и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре. Плюс поймёте, как хорошо вы программируете и какие проблемы решаете, даже не задумываясь.
Будем разбираться на простом примере.
Точка А: Сервис Service записывает логи в файл с помощью класса FileLogger:
class FileLogger {…}
class Service {
FileLogger logger=new FileLogger();
}
Сделаем код чуть лучше:1️⃣ Dependency injection
это когда компоненты создаются не внутри класса, а передаются в конструкторах или сеттерах. Перенесём инициализацию логгера в конструктор:
class Service {
FileLogger logger;
Service (FileLogger logger) {
this.logger= logger;
}
}
✅ Класс не занимается инициализацией логгера2️⃣ Dependency invertion
Буква D в аббревиатуре SOLID, формулировка состоит из двух частей:
▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции.
Суть: пусть сервис работает не с конкретным логгером, а с интерфейсом
interface Logger {…}
class FileLogger implements Logger {…}
class Service {
Logger logger=new FileLogger();
}
✅ В интерфейсе доступно меньше методов, поэтому его проще использовать✅ Реализацию легко заменить
✅ Оба класса проще тестировать
Почему используется слово "абстракция"? Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс самый подходящий вариант.
3️⃣ IoC - Inversion of Control
В маленьких программах жизнь начинается в методе main(). Программист создаёт объекты, вызывает методы, все шаги явно прописаны.
Inversion of Control - это когда ход выполнения программы задаёт фреймворк. Spring смотрит на классы и аннотации, а затем создаёт объекты, связывает их вместе и не даёт программе завершиться.
@Component class FileLogger {…}
@Component class Service {
@Autowired
FileLogger logger;
}
✅ Меньше скучного кода✅ Низкая связность - код легко менять, тестировать и переиспользовать
Spring создаёт обёртки классов и работает через Dependency Injection. Можно и по-другому: через паттерн фабричный метод, стратегия или сервис локатор.
⚔️Историческая справка.
Сервис локатор иногда встречается в легаси проектах. Это когда компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы.
class ServiceLocator {
private static Logger logger=…
public static Logger getLogger() {
return logger;
}
}
public class Service {
private Logger logger = ServiceLocator.getLogger();
}
Резюме:🔸Dependency injection - класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
🔸Dependency invertion - класс работает с другими компонентами через интерфейс
🔸Inversion of Control - ход программы задаёт фреймворк. Соединять компоненты может Dependency injection, фабричный метод, стратегия или сервис локатор.
❗️Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
#теория
👍4
IDEA: 4 метода для рефакторинга
IDEA - очень продвинутая IDE . Методов рефакторинга так много, что у них даже отдельная вкладка в меню. На каждом проекте точно пригодится:
1️⃣ Переименовать класс, метод, переменную или файл
Правой кнопкой по имени → Refactor → Rename
Имя изменится везде, где упоминается сущность.
2️⃣ Выделить константу
"Магические числа" в коде - плохая практика, лучше читаются именованные константы.
Правой кнопкой по числу → Refactor → Introduce Constant
Выделяем нужные строки → правый щелчок мышки → Refactor → Extract Method...
И наоборот
4️⃣ Убрать лишние методы и переменные, "уплотнить" код
Правый щёлк → Refactor → Inline Method/Inline Variable
IDEA - очень продвинутая IDE . Методов рефакторинга так много, что у них даже отдельная вкладка в меню. На каждом проекте точно пригодится:
1️⃣ Переименовать класс, метод, переменную или файл
Правой кнопкой по имени → Refactor → Rename
Имя изменится везде, где упоминается сущность.
2️⃣ Выделить константу
"Магические числа" в коде - плохая практика, лучше читаются именованные константы.
Правой кнопкой по числу → Refactor → Introduce Constant
❌ for(int i=0;i<100;i++)3️⃣ Перенести код в отдельный метод
✅ for(int i=0;i<HLIMIT;i++)
Выделяем нужные строки → правый щелчок мышки → Refactor → Extract Method...
И наоборот
4️⃣ Убрать лишние методы и переменные, "уплотнить" код
Правый щёлк → Refactor → Inline Method/Inline Variable
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