JavaRush /Курси /Java Syntax Zero /Нюанси роботи різних операторів

Нюанси роботи різних операторів

Java Syntax Zero
Рівень 9 , Лекція 7
Відкрита

1. Булеві операції

Оператори | і & можна застосовувати не лише до чисел, але й до виразів типу boolean.

Проте, як ти вже знаєш, для логічних виразів є оператори && і ||. Що ж нам дасть використання | замість || і використання & замість &&? Чи є в цьому сенс? Виявляється, є. Іноді.

Логічні оператори || і && виконуються зліва направо за так званим ледачим принципом.

(вираз1) || (вираз2) || (вираз3)

Якщо вираз1 дорівнює true, немає сенсу обчислювати вираз2 і вираз3: результат усе одно буде true.

Тому при обчисленні виразів (а вони обчислюються по порядку зліва направо), як тільки ми отримали true, обчислення решти виразів пропускається. І якщо в виразах вираз2 і вираз3 були виклики якихось функцій, то ці функції не викликаються!

Те саме і для логічного оператора &&:

(вираз1) && (вираз2) && (вираз3)

Якщо вираз1 дорівнює false, немає сенсу обчислювати вираз2 і вираз3: результат усе одно буде false.

Це важливий момент, який дозволяє писати речі типу:

String s = null;
if (s != null && s.length() > 0) {

У прикладі вище ми ніколи не отримаємо NullPointerException, бо вираз s.length() виконується лише якщо перша частина s != null дорівнює true.

Якщо s виявиться рівним null, зліва від оператора && буде значення false, і результат усього логічного виразу буде false, тому обчислення другої частини (s.length() > 0) не відбувається.

Отже, до чого це все:

Якщо в логічному виразі використовувати оператор | або оператор &, то ледаче обчислення не відбувається — у будь-якому випадку обчислюються всі вирази.



2. Пріоритет операцій у Java

Як ти, мабуть, пам'ятаєш зі шкільного курсу математики, операція множення має вищий пріоритет, ніж операція додавання. Дужки ж мають ще вищий пріоритет: спочатку обчислюються вирази в дужках, а потім уже множення/ділення та додавання/віднімання.

Оператори в Java також мають пріоритет. Тільки: а) їх трохи більше, б) у деяких операторів дії виконуються зліва направо, а в інших — справа наліво.

Ось як виглядає таблиця з усіма операторами Java:

Категорія Оператор Асоціативність
Постфікс () [] . Зліва направо
Унарний ++ -- ! ~ Справа наліво
Множення * / % Зліва направо
Адитивний + - Зліва направо
Зсув >> >>> << Зліва направо
Реляційний > >= < <= Зліва направо
Рівність == != Зліва направо
Побітове І (AND) & Зліва направо
Виключаюче АБО (XOR) ^ Зліва направо
Побітове АБО (OR) | Зліва направо
Логічне І (AND) && Зліва направо
Логічне АБО (OR) || Зліва направо
Умовний ?: Справа наліво
Присвоєння = += -= *= /= %= >>= <<= &= ^= |= Справа наліво
Кома , Зліва направо

У верхньому рядку йдуть найпріоритетніші оператори. Круглі дужки () використовуються для явного встановлення пріоритету. Квадратні дужки [] використовуються для індексування змінної-масиву. Оператор . використовується для отримання полів і методів у посиланні на об'єкт або клас.

Чим нижче оператори розташовані в таблиці, тим нижчий їхній пріоритет.

Якщо ти використовуєш кілька операторів у своєму виразі, не лінуйся: став дужки.

Так, у Java можна написати щось типу if (a & 1<<b > ~c), але так робити не варто. Ти пишеш код не тільки для компілятора, але й для інших програмістів. Чим код читабельніший, тим краще.



3. Префіксний та постфіксний інкремент

Як ти вже знаєш, у Java є оператор інкременту (++) і оператор декременту (--), які збільшують (і зменшують) значення змінної на 1.

Чого ти, скоріш за все, не знаєш, так це того, що існує два види цих операцій: префіксні – оператор ставиться перед змінною, і постфіксні — оператор ставиться після змінної. І працюють ці оператори трохи по-різному.

У Java можна написати такий вираз:

int a = 5;
int b = a++;

Якщо оператор ++ стоїть після змінної і змінна бере участь у якомусь виразі (як у прикладі вище), то у виразі буде використано поточне значення змінної, а тільки потім вона буде збільшена на 1.

Іншими словами, відбудеться приблизно це:

int a = 5;
int b = a;
a = a + 1;

Тобто, b матиме значення 5, а не 6, як це могло здатися спочатку.

Якщо ж оператор ++ стоїть перед змінною і змінна бере участь у якомусь виразі, вона спершу збільшується на 1, а вже потім її значення бере участь у виразі.

int a = 5;
int b = ++a;

Приклад вище еквівалентний наступному прикладу:

int a = 5;
a = a + 1;
int b = a;

Ось тут уже значення b буде рівне 6.

Є навіть один приклад, який використовують Java-програмісти, щоб визначити, чи їхній співрозмовник теж Java-програміст:

int a = 5;
int b = ++a++a;

Так, так теж можна писати.

Цей приклад чудово скомпілюється і перетвориться приблизно в це:

int a = 5;

a = a + 1;
int v1 = a;

a = a + 1;
int v2 = a;

int b = v1 + v2;

Для оператора -- усе точно так само.



Коментарі (22)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Anonymous #3425794 Рівень 11
14 грудня 2024
Прошу уваги☺️ Цей коментарій не відповідь, а для того щоб ви зрозуміли, як воно працює. Все дуже просто. а = 3 b = А++ + (--А * А++); // останнє А++ нічого не набуває воно таке саме як і А, бо постінкремент; Значення А набувається зліва направо ігноруючи арифметичні оператори (+-*/) -перше А++ = 3, постдекремент перекидає одиничку на наступне А; -наступне (друге) --А отримало +1 від першого, але за рахунок предекремента одразу відняло -1 і стало дорівнювати 3 знову; -трете А++ нічого не отримає, бо постінкремент додасть до наступного A, а таких в нас немає(; Тепер розв'язок йде за звичними нашому мозку діями 3 + (3 * 3) = 12 Бажаю успіхів!!!
IronMan57 Рівень 28
1 грудня 2024
Ніяк не міг розібратись з порядком виконання операцій в останній задачі. Нарешті звернувся до ChatGPT за поясненням, давши йому тільки дві початкові стрічки коду, задані в завданні (не в розв'язку): int a = 3; int b = a++ + (--a * a++); І... сюрприз - ChatGPT написав, що в результаті виконання цих команд змінна b матиме значення 11. Прийшлось його тицнюти носом в сліди його ж життєдіяльності. Після чого ChatGPT перевзувся, вибачився і написав, що все таки буде 12 і дав слідуюче пояснення: Пошаговый разбор выражения int b = a++ + (--a * a++); Начальное значение a = 3. Операция a++: Постфиксный инкремент. Возвращается текущее значение a, затем оно увеличивается. Возвращаемое значение: 3. Значение a становится 4. Операция --a: Префиксный декремент. Уменьшает значение a перед использованием. Значение a становится 3. Возвращаемое значение: 3. Операция a++: Постфиксный инкремент. Возвращается текущее значение a, затем оно увеличивается. Возвращаемое значение: 3. Значение a становится 4. Вычисление --a * a++: После декремента значение a равно 3. Умножение: 3 * 3 = 9. Сложение: Суммируется результат первого постинкремента (3) и результата умножения (9). Результат: 3 + 9 = 12. Присваивание: Переменной b присваивается значение 12. Итог: Значение переменной b: 12. Значение переменной a после выполнения выражения: 4. Ошибка в моём разборе: Я неправильно учёл изменение значения переменной a при постинкрементах и декременте, что привело к ошибке в интерпретации результата умножения и сложения. Правильная последовательность операций: Операция a++ (возвращает 3, a становится 4). Операция --a (уменьшает a до 3, возвращает 3). Операция a++ (возвращает 3, a становится 4). Умножение --a * a++ (результат: 3 * 3 = 9). Сложение a++ + (...) (результат: 3 + 9 = 12). Присваивание значения b.
IronMan57 Рівень 28
1 грудня 2024
Після того, як я попросив уточнити, яка з операцій a++ (у виразі їх дві) виконується першою, він ще двічі перевзувався, даючи різні кінцеві відповіді. Після згадався анекдот: "Зустрілись два політологи: - Що взагалі відбувається? - Зараз я тобі все поясню. - Пояснити-то я й сам можу. Я тебе запитую: Що, #$&, відбувається?"
Гаркін Рівень 14
20 березня 2024
Задачу я вирішив, але що за чим йде у цьому рядку

number < 15 & number > 25 & (number += 15) < 25
я не розумію. Сподіваюсь, що вирішив усі задачі лекції я розберусь з цим (і зможу це написати). Буду вдячний, якщо хтось пояснить цей рядок.
Гаркін Рівень 14
22 березня 2024
Дивимось пріоритет операцій та лекцію з додаткових матеріалів "Побитовые операции в Java" https://javarush.com/groups/posts/1925-pobitovihe-operacii . number < 15 & number > 25 & (number += 15) < 25 пріоритет операції у дужках потім пріоритет у порівнянь, тобто number < 15 це false number > 25 це false 30 < 25 це false отримаємо false & false & false Для перевірки результату я створив: boolean a = false; boolean c = true; boolean b = a & a & a; System.out.println(b); А який результат? А спробуйте :) . Ну і задля експерименту c & c & a А чому такі результати? Ось відповідь: … The Java Virtual Machine encodes boolean array components using 1 to represent true and 0 to represent false. …
gummer Рівень 11
6 березня 2024
Я не розумію. В мене виходить 20, але задачу перевірку не проходить оО

int b = ++a + (a++ * --a);
Апд.: не стримався та глянув правильну відповідь

int b = ++a + (a-- * ++a);
Вибачаюсь, а ви колись про комутативність множення чули? Т_Т
Гаркін Рівень 14
22 березня 2024

У методі можна змінювати лише постінкремент на преінкремент
 і навпаки, а також постдекремент на предекремент і навпаки.
Тобто ти порушив умови завдання.
4 лютого 2024
Решал так: В каждом случае мы можем просто менять с одной позиции на другую (пост/пред). При этом есть только два значения в итоге. Пробежался по 3 переменным, у которых в конечном итоге было по 2 варианта. Что-то вроде: 3 || 4 + (3 || 4 * 3 || 4) = 20 (тут "||" я написал как "или"). Решая математический ребус получаем 4+(4*4) = 20. Осталось написать код. З.Ы. Помог сильно в решении первый запуск. Думал 7, по факту 12. Пробежался по логике, поправился и вуаля.
Serhii Horban Рівень 18
11 липня 2023
логіка компілятора така: int a = 3; /*int b = ++a + (a-- * ++a); цей вираз можна розбити на 3 менших вирази (v1 + (v2 * v3)) */ a = a + 1;// (++а) спочатку збільшуєм а на 1 int v1 = a;// надаємо значення першому виразу int v2 = a;// надаємо значення другому виразу a = a - 1;// (а--) зменшуєм а на 1 a = a + 1;//(а++) збільшуєм а на 1 int v3 = a;// надаємо значення третьому виразу int b = v1 + (v2 * v3);// а тут уже іде нормальний пріоритет І виходить що, префіксний і постфіксний інкремент і декремент взагалі поза пріотритетом?
Anonymous #3289637 Рівень 19
24 квітня 2023
int a = 3; int b = ++a + (a-- * ++a); повинно бути 16 ? Але мене написало що вийшло 20 та пропустило задачу. мб я чогось не розумію
Viacheslav B. Рівень 1
10 лютого 2024
зверні увагу що а-- це постфіксний інкремент, як пишут у лекції - Якщо оператор ++ або -- стоїть після змінної та водночас ця змінна входить в якийсь вираз (як у прикладі вище), то у виразі буде використано поточне значення змінної, а тільки потім його буде збільшено++ або зменшено на 1. тобто (a-- * ++a) - це (4 * 4)
Abver Рівень 4 Expert
18 грудня 2022
Я думав що зрозумів приоритетність, але походу ні. От поясніть мені хтось, int a = 3; int b = ++a + (a * a); чому результат 20. я думав що спочатку 3*3=9 і потім +4 але щось я неправильно зрозумів
Алексей Рівень 40
21 грудня 2022
int b = a++ = (a*a) Це теж саме, що a=a+1; a + (a*a)
Yaroslav Tkachyk Рівень 23 Expert
24 грудня 2022
Якщо коротко: 1) в цій лекції є пріоритет операцій. 2) Операції в своїй переважній більшості виконуються зліва направо. 3) щоб не писати вірний код - оформлю так: операція №1 - префіксний інкремент числа а - число а збільшено на одиницю операція №2 - (в дужках результат операції №1 і постфіксний декремент) - число а спочатку використано, а потім зменшено на одиницю)) операція №3 - префіксний інкремент результату операції №2 - число а спочатку збільшено на одиницю а потім стає результатом операції №3 операція №4 - операція множення в дужках результату операції №2 та результату операції №3. операція №5 - тут не впевнений в черговості: варіант (а) - чи спочатку int b присвоюється результат операції №1 (тоді скажемо це буде крок №1.5) і потім відбувається додавання результату операції №4. варіант (б) - відбувається сумування результатів операцій №1 та №4. І тоді вже крок №6 - це присвоювання результату операції №5 як значення для змінної b. Може хто підкаже чи є функціонал в IDEA щоб проглянути виконання виразу покроково, щось на кшталт перегляду покроковості циклу під час використання дебагера?
Roma Chernesh Рівень 16
25 грудня 2022
Поясніть хтось, хто це розуміє (бо сам аніяк не вкумекаю) Якщо запустити код без змін, то він видає 12. АЛЕ: a = 3; a++ + (--a * a++); Хіба це не так працює? 1) --а = 2; 2) a++ = 3; 3) --a * a++ = 2 * 3 = 6; 4) a++ = 4; 5) a++ + (--a * a++) = 4 + ( 2 * 3) = 10 Тоді звідки 12? Задачу-то вирішив методом научного тику (три всього змінні). Але щось логіку не зрозумів: - В нас же спочатку йдуть інкріменти у душках. (1 , 2) - Потім вони перемножаються. (3) І потім у кніці додаємо це до інкріменту. (4, 5) Хіба ні?
Yaroslav Tkachyk Рівень 23 Expert
26 грудня 2022
Спочатку виконуємо операцію після знаку =. a = 3; b = a++ + (--a * a++); Послідовність кроків наступна: 1) b = а (тобто 3) 2) а++ (а стає 4) 3) пішли операції в дужках - --а( а стає 3) 4) операція множення а*а (добуток в дужках стає 9) 5) а++ (а стає 4) 6) остання операція додавання b + добуток в дужках (3+9) 7) Результат 12.
Yaroslav Tkachyk Рівень 23 Expert
26 грудня 2022
в тебе в 3 дії (--a * a++ = 2 * 3 = 6;) є одразу неточність, оскільки: якщо початково а == 3, то логіка дії в дужках наступна: --а (а = а-1; а == 2) а*а = 4 а++ (а = а+1; а == 3)
Roma Chernesh Рівень 16
26 грудня 2022
Величезне спасибі! Все думав-гадав, чи дужки "( )" мають перевагу над ++ чи ні. Ото тепер все стало на свої місця!
777 Рівень 16
23 вересня 2022
хто мені пояснить чому цей код не прходить провірку? int a = 3; int b = ++a + (a++ * --a);
Саша Рівень 30
9 жовтня 2022
в дужках має бути префінкремент і постдискремент. А в тебе зараз виходить б = 10
Yaroslav Tkachyk Рівень 23 Expert
24 грудня 2022
Якраз його код дасть результат 20. Проте не виконана умова: у методі можна змінювати лише постінкремент на преінкремент і навпаки, а також постдекремент на предекремент і навпаки.
Lipovskyi Volodymyr Рівень 36
11 травня 2022
Після попередньої лекції просто бальзам на душу)))