Грамота от Кузьмича – Telegram
Грамота от Кузьмича
214 subscribers
54 photos
1 video
20 links
🍵Java.
🤖Автотестирование.
😆Мемасы.

Я - Александр Кузьмичев. Просто и с юмором рассказываю об IT, а еще веду свою школу "Java + Автотестирование"

Ссылки на все соц. сети:
https://devray.taplink.ws/

TG: @NeonAether
Download Telegram
Четко про классы и объекты!🤓🎓

Класс - это чертеж, по которому строятся объекты этого класса. Чертеж рассказывает какие поля (свойства) и методы (возможности, функционал) есть у этого объекта.
Чертеж существует в единственном экземпляре. А вот объектов на базе чертежа можно создать сколько угодно.

Так же и архитектор один раз создает чертеж дома (класс), по которому затем могут быть построены десятки и сотни реальных домов (объектов).🗒🏠🏠🏠

Так же как и у повара может быть форма для печенья в виде звездочки. Форма одна (класс), а вот печенек-звездочек (объектов) с ней можно сделать сколько угодно⭐️🍪🍪🍪

P.S. на инфографике зашифровано название фильма, можете испытать себя:)
🔥51
Время подумать о пицце о статических и нестатических полях!🍕И когда применять какие при создании своего класса. Верный ответ с объяснением будет опубликован на канале позднее:)
🔥6🤔1
🍕Что бы разобраться в разнице статики и нестатики - надо понимать разницу между классом и объектом.

Статические поля - поля класса. А нестатические - поля объекта. И раз объектов можно создать много - то у каждого из объектов будет свой набор нестатических полей. А вот статические поля - общие, и сколько бы объектов мы ни создали - они останутся в единственном экземпляре.

Небольшой момент - рассуждать о статичности/нестатичности полей мы будем исходя из образа пиццерии, в меню которой есть несколько разных пицц и штат поваров.

🔢процентСгоревшихПицц - каждому объекту пиццы такая информация точно не нужна! это статистика о созданных объектах - так пусть она хранится на уровне класса, в единственном экземпляре.
🔖названиеПиццы - пиццы которые мы готовим могут быть разными - пепперони, мясной пир, четыре сыра - и эту разницу, это свойство каждой пиццы - важно хранить в самом объекте. Поле названиеПиццы можно было бы сделать статическим только в случае если в нашей пиццерии пицца готовится по одному единственному рецепту.
🏋️вес - некоторые пиццы бывают тяжелее, некоторые легче - в зависимости от их размера и набора ингредиентов. поэтому вес - описание конкретного объекта
🧑‍🍳имяПриготовившегоПовара - сегодня на смене работают Гордон и Уолтер, и было бы здорово знать кто приготовил конкретный объект пиццы
🔥маркаПечиПоУмолчанию - ясен красен что такая характеристика - общая для всех пицц! Несем поле в статику.
🍕последняяПриготовленнаяПицца - в каждом объекте пиццы эта информация ни к чему. А вот в классе - пожалуйста!
📦размер - пиццы бывают маленькие, большие, и гигантские! Размер - характеристика конкретного объекта.
🔢количествоВыпеченныхПицц - опять статика, опять информация, которую не нужно дублировать в каждом объекте, которая скорее косвенно относится ко всем-всем-всем пиццам.
🧀естьСырныйБортик - еще одна щепотка индивидуальности каждого объекта пиццы! Как кот шредингера - может быть у пиццы, а может не быть. Нестатическое поле.
8🔥2👍1
🔥❄️Однажды мастер кунг-фу сказал своему ученику:
"Держи разум холодным, а клавиши - горячими."


Сегодняшний пост - про горячие клавиши в Intellij IDEA, они же хоткеи. И да, мастер был прав, потому что с горячими клавишами ты сможешь противостоять натиску даже 10000 срочных и коварных задач.

Разберу самые важные из хоткеев для начинающих:

Alt + Enter - Наведя курсор на любой участок кода - можно получить предложения идеи по альтернативам/улучшению/оптимизации этого кода. Чаще всего полезно нажать когда курсор наведен на подсвеченный красным код - идея предложит быстрый вариант решения проблемы.
Alt + Insert - Генерация фрагментов типового кода (конструкторы, геттеры-сеттеры, переопределение методов). Т.е. все то что нам скучно делать самим.

Shift + Shift (быстро дважды нажать шифт) - Открыть универсальный поиск. Можно искать все что угодно - и код, и текст, и команды и настройки в идее, и вообще все
Ctrl + Shift + F - Поиск по файлам проекта - более специализированный поиск

Ctrl + W / Ctrl + Shift + W - Увеличение/уменьшение выделения фрагмента кода. Удерживая Ctrl и последовательно нажимая W можно выделить текст от одного слова до целого метода и дальше до целого класса
Ctrl + D - Дублирование текущей строки / выделенного фрагмента. Самый полезный и экономящий время хоткей!
Ctrl + / - Комментирование текущей строки / выделенного фрагмента

Alt + Shift + клавиша вверх/вниз - Переместить строку / выделенный фрагмент выше или ниже на одну строку

Ctrl + B - Войти в определение метода, на котором стоит курсор
Ctrl + Alt + L - Автоматическое форматирование (правильные переносы и отступы в текущем классе)

Alt + 1 - Открыть/закрыть проектное дерево
Alt + 7 - Открыть/закрыть структуру класса

Есть еще много важных хоткеев, но для начала начни практиковать эти. В будущих постах продолжу список этих полезностей.

Бонусом - пара советов.
1. Если забыл хоткей - запусти глобальнй поиск (Double Shift) - и введи "Keyboard Shortcuts PDF" - это встроенная в Intellij IDEA шпаргалка по горячим клавишам.
2. Установи плагин "Key promoter X plugin" -
https://plugins.jetbrains.com/plugin/9792-key-promoter-x
Эта штука подобно сенсею будет каждый раз тебе подсказывать какие действия ты сейчас совершаешь мышкой, а мог бы заменить на хоткеи.


И помни: хоткеи > движения мышкой. Работая с клавиатурой и хоткеями ты программируешь быстро и стильно, подобно хищной и безжалостной кобре🐍
🔥10👍21
Дорогие друзья, с наступающим!❄️ Узнавать новое - наша ежедевная работа, но иногда - надо просто от всего отдохнуть, отключиться. Один мой друг поделился классной метафорой о том, что брать паузы на отдых - так же важно, как будучи за рулем автомобиля - останавливаться подзаправиться.

🚙Вовремя не заправишься (не отдохнешь) - поедешь "на парах", пропадет тяга, пойдут вибрации.
🚕Если и после этого не заправишься - просто встанешь намертво и будешь еле-еле толкать машину.
🚗Если заправишься абы чем - двигатель долго не проходит. И так далее.

Аналогии с жизнью чувствуете? Поэтому желаю вам всем качественно заправиться, отдохнуть и пополнить запас сил. Что бы быть готовыми к по настоящему великим делам и достижениям, которые заставят вас собой гордиться. Желаю вам счастливого Нового года, тяги к знаниям и твердой воли🎄🎄🎄

ПС В этой авто-метафоре, что бы не ехать непойми куда - неплохо еще и планировать свой путь. Коллеги подкинули бесплатный планер https://yearcompass.com/ru/ - мне немного помогло структурировать свои цели на 2024, возможно поможет и кому-то из вас. (впрочем до конца я его так и не заполнил, шутка ли, 20 страниц😁буду еще сидеть)
8🔥3👍2
Хелоу, на ночь глядя - о мапперах, библиотеке MapStruct и чайных делах.

Что делать если я тестирую API, и для одного и того же по смыслу предмета от одного эндпоинта приходит одна модель данных, а от другого другая?
На примере - представим APIшку магазина китайского чая🫖

У APIшки есть два эндпоинта:

1. эндпоинт для добавления нового сорта чая в ассортимент магазина
POST /tea
в теле запроса отправляется json с данными о добавляемом сорте чая, типа такого:
{
"newTeaName": "Жоу Гуй",
"type": "улун"
}


2. ☕️эндпоинт для получения рекомендуемого чая сезона
GET /teaOfTheDay
а в теле ответа возвращается объект:
{
"id":"2"
"teaName":"Жоу Гуй"
"type":"улун"
}

Чертовски похожие объекты, что там чай, что там чай, ну окей. Смоделировать json-объект отправляемого чая (1) и возвращаемого чая (2) можно такими нехитрыми ДТОшками с применением аннотации @Data (lombok):
@Data
public class Tea {
private String newTeaName;
private String type;
}
@Data
public class ShopTea {
private int id;
private String teaName;
private String type;
}


Мы видим что речь идет о практически идентичных объектах. Но их сложно сравнивать друг с другом по следующим причинам:
1. Tea & ShopTea - объекты разного типа
2. имена/состав полей не соответствуют друг другу или соответствуют не полностью (к примеру поля teaName & newTeaName хранят по сути одинаковую информацию но называются по разному)

А еще возможно что у вас уже написано немало методов-утилит для работы с классом Tea, но вот объект ShopTea туда уже не передать.
Задача перед нами - превратить объект типа ShopTea в объект типа Shop. Для таких превращений выделяют специальные классы-мапперы (mapper):

public class OldTeaMapper {
Tea shopTeaToTea(ShopTea shopTea) {
Tea tea = new Tea();
tea.setNewTeaName(shopTea.getTeaName());
tea.setType(shopTea.getType());
return tea;
}
}


Вроде и неплохо, но логику преобразования пришлось написать ручками. А самое главное - в жизни в json от сервисов - намного больше полей (десятки), и рукописного труда оказалось бы дофига. Тут и врывается MapStruct - для генерации таких классов-мапперов. Код для маппера с применением MapStruct будет выглядеть вот так:
@Mapper
public interface MapStructTeaMapper {
@Mapping(source = "teaName", target = "newTeaName")
Tea shopTeaToTea(ShopTea shopTea);
}

Причем логику преобразования совпадающих по имени-типу полей можно вообще не описывать - через @Mapping нам пришлось описать только особый случай, когда поля не совпадали по имени. Ну а если поле у первого объекта совпадает с полем второго и по типу и по имени - никаких аннотаций писать не надо.

Для работы этой библиотеки надо:
1. указать ее как зависимость
2. настроить ее работу с плагином компилятора
3. применить в коде

И все это, с пылу-с жару подготовленное для вас - находится здесь:
https://gitlab.com/RevRay/mapstruct-demo

Пункты 1 и 2 выполняются в pom.xml для Maven-проекта, а пункт 3 - код для разобранного выше примера - есть в классе MapStructTest.

Изучайте пример, читайте документацию (https://mapstruct.org/), пейте вкусный чай и пишите мапперы как преисполнившиеся в своем познании люди🤓
🔥8🤔1
Присылаю вам пламенный привет🔥💌 с прошедшей конференции Dump из Питера! На конфе было круто - есть треки докладов по фронт, бек-разработке, менеджменту и тестированию.
(А вот в Питере сейчас не круто - дубак и ветруган🥶)

Доклады огонь, спикеры топ, но сегодня хочу поделиться именно Java-задачками, которые были на стенде у компании PVS-Studio (делают статические анализаторы кода). Суть такая - есть 4 фрагмента реального кода из разных opensource-проектов, в каждом из которых есть бага🐞
Задача - найти ее.

Задачки непростые, для разработчиков, поэтому если немного подумав так и не нашли проблемы в коде - вот вам подсказки:

1 - ищем нестабильность в работе цикла
2 - проблема кроется в математике
3 - бага кроется где-то в области всего блока if
4 - смотрим на операцию побитового сдвига и анализируем, что с ней не так

Позже разберу правильные ответы - заодно сможете себя проверить и наградить виртуальной/шоколадной медалькой🥇🥈🥉
🔥71👍1
Грамота от Кузьмича
Присылаю вам пламенный привет🔥💌 с прошедшей конференции Dump из Питера! На конфе было круто - есть треки докладов по фронт, бек-разработке, менеджменту и тестированию. (А вот в Питере сейчас не круто - дубак и ветруган🥶) Доклады огонь, спикеры топ, но сегодня…
Счастливого понедельника! Как обещал - публикую ответы к задачкам выше:

#1
Имеем цикл while (count < 4) - который опирается на значение count, которое никогда внутри цикла не меняется. Значит цикл бесконечный. Например попав в цикл с count = 3 - count всегда будет оставаться = 3, значит условия цикла будет верно, значит мы будем в нем кружиться до тепловой смерти вселенной.

П.С. Вообще бесконечные циклы - это нормально, до тех пор пока в них у вас описаны способы выхода, в один из которых можно гарантированно прийти - это может быть и break и return, выброс исключения, да даже простихосспаде System.exit(0) - что угодно, лишь бы из цикла можно было как-то выбраться. Либо рассчитывайте на то что приложение придется прерывать вручную.

#2
return capitalized / words.size() < 0.2;
Тут пара проблем. Во-первых - можно нарваться на ArithmeticException при делении на 0.
Но важнее здесь то что при делении capitalized / words.size() происходит деление целого числа (int) на целое число (int) и результатом будет тоже int, а значит никаких дробей мы ни при каком раскладе не получим и сравнение не принесет того смысла который мы хотели. Например:
capitalized = 1;
words.size() = 5;
capitalized / words.size() = 1 / 5 = 0 - потому что произошло целочисленное деление.

Решением может стать явное преобразование одного из int к double, что бы результат деления тоже дал double:
return (double) capitalized / words.size() < 0.2;

#3
if (endKey[i] < 0xff) - вот корень зла. Здесь мы имеем условие, которое будет выполняться всегда - а значит либо это условие описано неправильно, либо if тут вообще лишний. Почему условие будет выполняться всегда? Разбираем значения и типы данных:
byte[] endKey - массив байт,
endKey[i] - один байт из массива, может хранить значение от -128 до 127
0xff - число в 16-ричной форме записи, в человеческом виде это число 255.
Собственно, читаем исходную строку кода с if новым взглядом:
if (endKey[i] < 0xff)
if ([значение от -128 до 127] меньше [255]) - очевидно что это условие будет выполняться всегда.

#4
Проблема кроется в этой строке:

val = val | (1 << j);

Начну с вводной. Часть 1 << j - по сути аналог математического выражения 2^j (2 в степени j). В Java нет оператора степени, но есть метод Math.pow(). Без оператора побитового сдвига строчка кода выше могла бы выглядеть как:

val = val | (1 * Math.pow(2, j));
или просто
val = val | Math.pow(2, j);

Почему тут вообще решено было использовать операцию левого битового сдвига << ? Многим эта тема кажется стремной и неоправданной, но представьте что ваш метод - "горячий", его вызовы в приложении происходят по 10000 раз в секунду, а то и больше - тогда конечно вы будете гнаться за каждой милисекундой в ущерб читаемости. Загляните ради любопытства в реализацию метода Math.pow() - и ужаснитесь сколько всего там происходит. Оператор же побитового сдвига прост как сапог - двигает биты в памяти влево на N позиций.

Поэтому побитовый сдвиг может заменять обычные арифметические операции умножения/деления числа на двойку или степени двойки когда вам реально нужен скоростной код. Кейс правда точно не про обычные API/UI автотесты, у нас таких жестких требований к производительности не будет почти никогда.

Теперь про причину ошибки в упомянутой строке - проблема в интовом типе 1 (единицы) в выражении:
val = val | (1 << j);

Все числовые литералы - неявно int-ы (если только у них не указаны постфиксы: L, l (для long), D, d (для double), F, f (для float)).
1 << j - означает что единица целочисленного 4-байтного типа (т.е. 00000000 00000000 00000000 00000001 в двоичном виде) будет сдвинута на j разрядов влево. Например:
1 << 2 = 00000000 00000000 00000000 00000100 в двоичной системе = 8
1 << 13 = 00000000 00000000 00100000 00000000 в двоичной системе = 8192
Если j окажется больше или равно 31 - мы получим переполнение типа, знакомое нам и по обычным арифметическим операциям (ну например System.out.println(2000000000 + 2000000000); //result: -294967296).
👍2🔥1
Грамота от Кузьмича
Присылаю вам пламенный привет🔥💌 с прошедшей конференции Dump из Питера! На конфе было круто - есть треки докладов по фронт, бек-разработке, менеджменту и тестированию. (А вот в Питере сейчас не круто - дубак и ветруган🥶) Доклады огонь, спикеры топ, но сегодня…
Ну и окей, нам то че с того? Вот мы с вами когда пользуемся операторами +,-,*,/ - никогда не паримся, просто держим в голове что если числа с которыми мы делаем вычисления будут слишком большими - надо будет или делать до вычисления какую-нибудь проверку, или работать с long/BigInteger, или с Math.addExact(), тут джекичанских путей решения этой задачи хватает. В чем тогда бага этой строки?
val = val | (1 << j);

Если представить это выражение чисто в виде используемых типов - проблема станет очевиднее (нет):
long = long | (int << int);
Целевой тип с которым мы работаем - long. А в части (1 << j) - мы получаем результат int, который воткнется в проблему переполнения раньше чем long. Поэтому что бы поправить ошибку - в коде надо явно указать что побитовый сдвиг мы делаем для long:
val = val | (1L << j);

Похожая ошибка на более простом кейсе - как если написать код:
int a = ...; //любое значение
long result = 2 * a; //результат умножения - int

И расстраиваться почему это у нас для больших чисел некорректно идет умножение. Фикс:
long result = 2L * a; //результат умножения - long

Выводы. Их несколько.

- Математика, типы, циклы и условия - только и ждут чтобы поиметь вас, поэтому знайте врага в лицо и будьте бдительны:)
- Баги встречаются везде, даже в крупнейших проектах. Тесты и автотесты - самое годное оружие против них. Напиши автотест - сделай мир лучше!
🔥41🙏1
Зимой распускаются не только подснежники🌼.

Мой план - стартовать экспресс-курс по продвинутой Java + автоматизации. Эдакая ступенька для роста с Junior в Middle. Сейчас определяю состав тем и технологий. Курс будет в записи, но первый поток - проведу в живую в zoom, единственный раз. Так что успевайте вписаться на рок-перфоманс🤘

Голосовалка за интересующие темы тут - https://forms.gle/Z3HSL3GhLJSkkc3G9 - топ тем из голосования войдут в курс.

Запись в список ожидания - в телегу @NeonAether. О старте продаж узнаете первыми и в личку. И следите за обновлениями в канале - об открытии набора, утвержденной программе курса и дате старта - инфа появится вот прям здесь, в этом канале🎯
🔥6👍3
“Инстанцио!” - быстро выкрикнул Гарри Поттер, взмахнув палочкой и добавив в pom.xml новую зависимость.💫

В нашем арсенале волшебника разработчика должно быть достаточно заклинаний библиотек, что бы разрабатывать быстро и не изобретать велосипеды. Добавим себе в копилку еще одну.

Instancio - библиотека про генерацию тестовых данных. https://github.com/instancio/instancio

“Но а как же Faker?” - спросите вы. А я поясню.
Faker (com.github.javafaker) в первую очередь генерирует отдельное атомарное значение, кусочек данных. Телефонный номер, email-адрес, строку ФИО, GPS-координату.
Instancio - предназначен для создания сложных структур данных, т.е. объектов с полями, с уровнями вложенности.

Просто для сравнения - как бы мы генерировали объект класса Person(firstName, lastName, age, phoneNumber) со случайными значениями полей с использованием faker и с использованием instancio:

🔴Faker

Faker faker = Faker.instance();

Person person = new Person();
person.setFirstName(faker.name().firstName());
person.setLastName(faker.name().lastName());
person.setAge(faker.number().randomDigit());
person.setPhoneNumber(faker.phoneNumber().phoneNumber());

🔵Instancio
Person person = Instancio.create(Person.class);


Конечно не обойдется без нюансов. По умолчанию Instancio будет заполнять поля объекта полностью рандомными данными, в то время как с Faker мы будем получать данные, более похожие на реальные, живые. Для примера - как будут выглядеть объекты Person, сгенерированные двумя способами выше:

Person{firstName='Haydee', lastName='Kertzmann', age=4, phoneNumber='067.506.7057'} //для faker
Person{firstName='HRDYGWQP', lastName='UYEOXLEO', age=8229, phoneNumber='BBCCSDU'} //для instancio

Но каждому инструменту - свое применение. Резюмирая: Faker - про генерацию фейковых данных (но реалистично выглядящих). Instancio - про создание объектов, с полями, забитыми случайными данными (что может быть более актуально в unit-тестировании).

Саму зависимость Instancio можно найти здесь: https://mvnrepository.com/artifact/org.instancio/instancio
Дополнительно почитать про возможности и настройку библиотеки - тут: https://www.instancio.org/user-guide/#creating-objects (боже храни людей которые пишут документацию).
👍6🔥31
Что на картинке? Монитор центра управления полетами на Байконуре?🚀
Упс, это вкладка Performance в Chrome DevTools. По сути - история процесса построения и отрисовки страницы.

Сегодня заглянем во внутрянку как браузер превращает прилетевший текст HTML, CSS, JS - в сочную картинку. Упрощенно, шаги такие:

1. сборка DOM-дерева на базе HTML - строим объекты на базе каждого узла.
2. сборка CSSOM-дерева на базе CSS - строим объекты на базе каждого правила.
3. Render Tree - сборка дерева рендеринга. По сути это применение стилей к объектам DOM, т.е. объединение DOM + CSSOM
4. Layout - расчет размеров элементов, их расположения и взаимного размещения.
5. Paint - подготовка и прорисовка моделей элементов в растровые картинки.
6. Compositing - группировка полученных картинок в слои и отображение этих слоев на экран.

И еще - мы взаимодействуем со страницей, а не просто любуемся. Поэтому тык на любую кнопку может привести к перерисовке страницы и повторению всех шагов выше.

Так что тыкайте кнопки экономно, берегите ваш CPU😜
1🔥81👍1
Разминаем мозги, они нам нужны что бы скролить ленту с мемами и котиками!🐈🐈‍⬛
long a = List.of(0,1,2,3,4)
.stream()
.map(i -> Integer.valueOf(i++))
.filter(i -> i%2 == 0)
.count();
System.out.println(a);
Что будет результатом выполнения кода выше?
Anonymous Quiz
28%
2
20%
3
28%
0, 2, 4
4%
4
20%
2, 4
🔥3
Подъехали сладкие темы!🍪🍭🍫

Cookie (печенька, кука) - кусочек информации, которую бразузер получает от сервера, и дальше передает при любом запросе к этому серверу. Например, сервер может пользуется куками что бы пометить нас как конкретного пользователя и в дальнейшем узнавать, и даже что-то помнить о нас. Типа как дал кликуху во дворе.

Очень упрощенно, кука содержит:
1. Данные - имя свойства и его значение. В терминах Java можно думать об этом как о строковой переменной у которой есть имя и значения.
2. Адрес сайта к которому эта кука относится
3. Срок действия куки

Что может храниться в куки? Да любая дичь😄 Это просто строка в которой могут быть любые символы. Но самые частые кейсы данных в куки такие:

- 🧑кто ты такой (может хранить session id, токен, или еще какой удостоверяющий документ)
- ⚙️какие настройки сайта используешь (язык, светлая/темная тема)
- 📝какое состояние сайта у тебя сейчас (например в корзине покупок лежит три шерстяных носка)


Жми 🔥 что бы разжечь пожар, в котором родится видос с подробным разбором темы😉
🔥17👍2