- Логічні оператори в Java
- Оператор логічного заперечення!
- Логічне І - &, а також умовне І - &&
- Логічне АБО - оператор |, а також умовне АБО - оператор ||
- XOR - логічне виключне АБО - оператор ^
- Пріоритет логічних операцій
- Складні логічні вирази
- Порозрядні (побітові) оператори
- Порозрядні оператори і | і ^
- Додатковий код
- Побітовий оператор заперечення ~
Логічні операції у Java
Логічні операції виконуються з допомогою логічних операторів. Пробачте вже за тавтологію, але справи йдуть саме так. Основні логічні операції (у програмуванні та математиці) можна застосовувати до логічних аргументів (операндів), а також складати складніші висловлювання, подібно до арифметичних дій над числами. Наприклад вираз:
(a | b) | (c < 100) & !(true) ^ (q == 5)
є складним логічним виразом з чотирма операндами: (a | b)
, де а
і b
— змінні типу boolean
(c < 100)
(true)
(q == 5)
У свою чергу, простий логічний вираз (a | b)
також складається з двох аргументів-операндів. Логічний операнд - це вираз, про який можна сказати, що воно є істинним або помилковим, true або false . Говорячи мовою Java, логічний операнд - це вираз типу boolean
або Boolean, наприклад:
(2 < 1)
- логічний операнд, його значення дорівнює falsetrue
- логічний операнд, значення якого, очевидно, trueboolean a
- теж може бути логічним операндом, як і Boolean aint a = 2
- не є логічним операндом , це просто змінна типуint
String a = "true"
також не є логічним операндом . Це рядок, текстове значення якого -"true"
.
- Логічне заперечення , воно
NOT
або інверсія. Java позначається символом "!
" перед операндом. Застосовується одного операнду. - Логічне і , воно
AND
або кон'юнкція. Позначається символом&
між двома операндами, до яких застосовується. - Логічне або Java , воно ж -
OR
, воно ж - диз'юнкція. Java позначається символом “|
” між двома операндами. - Виключне або ,
XOR
, Сувора диз'юнкція. Java позначається символом “^
” між двома операндами. - У Java до логічних операторів можна віднести умовне або , що позначається як
||
, а також умовне і&&
.
==
не прийнято відносити до логічних. Увага! У Java логічні оператори&
і|
також^
до цілих чисел. У цьому випадку вони працюють дещо по-іншому і називаються порозрядними (або побітовими) логічними операторами. Про них ближче до кінця статті. Розглянемо таблицю з коротким описом кожного з логічних операторів Java, а нижче опишемо їх докладніше та наведемо приклади коду.
Оператор Java | Ім'я | Тип | Короткий опис | приклад |
---|---|---|---|---|
! |
Логічне "не" (заперечення) | Унарний | !x означає "не x". Повертає true якщо операнд є false . Повертає false якщо операнд є true . |
boolean x = true; Тоді // !x == false |
& |
Логічне І ( AND , множення) |
Бінарний | Повертає true якщо обидва операнди рівні true . | a = true; b = false; тоді a & b == false |
| |
Логічне АБО ( OR , додавання) |
Бінарний | Повертає true якщо хоча б один з операндів дорівнює true . | a = true; b = false; тоді a | b == true |
^ |
Логічне виключне АБО ( XOR ) |
Бінарний | Повертає true , якщо один і лише один з операндів дорівнює true . Повертає false , якщо обидва операнди рівні true або false . По суті, повертає true , якщо операнди різні. | a = true; b = false; тоді a ^ b == true |
&& |
Умовне І (скорочене логічне І) | Бінарний | Те ж саме, що і & , але якщо операнд, що знаходиться зліва від & false , даний оператор повертає false без перевірки другого операнда. |
|
|| |
Умовне АБО (скорочене логічне АБО) | Бінарний | Те ж саме, що і | , але якщо оператор ліворуч є true , оператор повертає true без перевірки другого операнда. |
Оператор логічного заперечення!
Цей оператор — унарний, тобто він застосовується до одного булевського виразу чи операнду. Зрозуміти його дуже просто, як будь-яке заперечення: оператор просто змінює значення висловлювання на протилежне. Таблиця істинності чи результати виконання операції заперечення:Значення a | !a |
false | true |
true | false |
public class Solution {
public static void main(String[] args) {
boolean a = true;
System.out.println(!a); // тут наш логічний вираз змінює значення протилежне
System.out.println(!false); // вираз не-false, як можна здогадатися, дорівнюватиме... чому?
System.out.println(!(2 < 5)); // Вираз (2 < 5) істинно, значить, його заперечення - хибно
}
}
Виведення програми буде наступне:
false
true
false
Логічне І - &, а також умовне І - &&
Логічне І або кон'юнкцію застосовують до двох виразів, і результат його дії буде істинним ( true ) тільки якщо обидва операнди істинні. Тобто, якщо один із операндівa
або b
дорівнює false , то вираз a & b
буде false незалежно від значення другого оператора. Якщо уявити, що true - це число 1, а false - 0, то оператор &
працює так само, як звичайне множення. Тому логічне І часто називають "логічним множенням". І, до речі, цей факт допомагає швидше запам'ятати роботу оператора &
та не плутати його з оператором логічного або |
. Та, вона ж - результат роботи оператора&
a | b | a & b |
true | true | true |
true | false | false |
false | true | false |
false | false | false |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(a & b); // якщо ми помножимо правду на брехню, то безперечно отримаємо брехню
System.out.println(a & c); // Щоправда на правду буде правда
System.out.println(false & (2 > 5));
System.out.println((2 < 5) & false);
// незалежно від правдивості висловлювання у дужках, у разі нам доводиться задовольнятися брехнею
}
}
Результат роботи програми:
false
true
false
false
Оператор &&
іноді називають "скороченим І". Він видає такий самий результат під час роботи з логічними операндами, як і оператор &
. Проте є різниця у самій його роботі. Так, ви вже встигли помітити, що якщо у виразі ( a & b
) операнд a
дорівнює false , то немає сенсу перевіряти значення операнда b
: результат операції точно буде false . Отже, якщо нам принципово не потрібне значення другого операнда, за допомогою &&
ми скорочуємо кількість обчислень у програмі. Якщо ми замінимо на прикладі всі оператори &
на&&
, Результат роботи буде точно таким же, але сама програма буде працювати трохи швидше (правда, ми цього не помітимо, так як мова йде про мілі-мікро ... Коротше кажучи, про дуже маленькі одиниці часу).
Логічне АБО - оператор |, а також умовне АБО - оператор ||
Оператор АБО Java позначається символом|
. Логічне АБО або диз'юнкцію застосовують до двох виразів, і результат його дії буде хибним ( false ) тоді і тільки тоді, коли обидва операнда хибні. Тут ми певною мірою спостерігаємо ту саму картину, що й у випадку з оператором &
, але з точністю до навпаки. Тобто якщо хоча б один операнд дорівнює true , то вираз a | b
гарантовано буде true незалежно від значення другого оператора. Якщо &
поводиться як логічне множення, то АБО - це логічне додавання, якщо уявити, що true - це 1, а false- 0. Тільки слід пам'ятати, що логічне додавання працює не так, як звичайне. 1 + 1 у разі одно не 2, а 1 (числа 2 у цій системі просто немає). Іноді диз'юнкцію розуміють як максимум з 0 і 1, і якщо хоча б один операнд дорівнює 1 ( true ), ми отримаємо саме true . Таблиця істинності АБО, вона ж - результат роботи оператора |
:
a | b | a | b |
true | true | true |
true | false | true |
false | true | true |
false | false | false |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(!a | b); // Скомпонуємо використання двох логічних операторів: a == true, отже, !a, як ми знаємо - це false.
System.out.println(a | c);
System.out.println((2 < 5) | false); // Вираз (2 < 5) істинно, значить, при будь-якому другому операнді ми отримаємо отримаємо справжній результат
System.out.println((2 > 5) | true);
}
}
Результат:
false
true
true
true
Якщо ми застосуємо оператор умовного АБО - ||
замість |
, ми отримаємо рівно той же результат, але, як і у випадку з умовним І &&
, він діятиме економно: якщо ми "нариваємося" на перший операнд рівний true , значення другого операнда не перевіряється, а відразу видається результат true .
XOR Java - логічне виключне АБО - оператор ^
XOR
, додавання по модулю 2, логічне виключає АБО, логічне віднімання, сувора диз'юнкція, порозрядне доповнення ... оператор ^
має багато імен в булевій алгебрі. Результат застосування цього оператора до двох операндів дорівнюватиме true , якщо операнди різні і false , якщо операнди однакові. Тому його зручно порівнювати з відніманням нулів ( false ) і одиниць ( true ). Таблиця істинності XOR
, вона ж - результат роботи оператора ^
:
Boolean a | Boolean b | a^b |
true | true | false |
true | false | true |
false | true | true |
false | false | false |
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean c = true;
System.out.println(!a ^ b); // Скомпонуємо використання двох логічних операторів: a == true, отже, !a, як ми знаємо - це false.
System.out.println(a ^ c);
System.out.println((2 < 5) ^ false);
System.out.println((2 > 5) ^ true);
}
}
Результат:
false
false
true
true
Пріоритет логічних операцій
Як і в математиці, в програмуванні оператори мають певний порядок виконання, якщо вони зустрічаються в одному вираженні. Унарні оператори мають переваги над бінарними, а множення (навіть логічне) над додаванням. Ми розташували логічні оператори у списку тим вище, чим вищий їхній пріоритет:!
&
^
|
&&
||
&
і |
) мають різний пріоритет:
public class Solution {
public static void main(String[] args) {
boolean a = true, b = true, c = false;
System.out.println(a | b & c);
}
Якби ми діяли зліва направо, тобто спочатку застосували б оператор |
, а потім — &
ми отримали значення false . Але насправді, якщо ви запустите цю програму на виконання, то переконайтеся, що висновок буде true , оскільки у оператора логічного І &
пріоритет буде вищим, ніж у оператора логічного АБО |
. Щоб не плутатися, треба пам'ятати, що &
поводиться як множення, а |
як додавання. Поміняти порядок пріоритету можна. Просто застосуйте дужки, як у шкільній математиці. Змінимо трохи код нашого прикладу:
public class Solution {
public static void main(String[] args) {
boolean a = true, b = true, c = false;
System.out.println((a|b)&c);
}
Що тут? Спочатку застосовуємо логічне додавання у дужках, а потім уже множення. Результатом буде false .
Складні логічні вирази
Зрозуміло, ми можемо комбінувати логічні вирази та оператори. Згадаймо вираз із початку статті:(a | b) | (c < 100) & !(true) ^ (q == 5)
Тепер воно виглядає не таке страшно. Напишемо програму, яка виводить його значення, попередньо визначивши значення a
, b
, с
і q
. Приклад обчислення значення складного логічного виразу
public class Solution {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
int c = 25;
int q = 2;
System.out.println((a|b) | (c < 100) & !(true)^(q == 5));
}
}
Зверніть увагу:змінна q
у нас відноситься до типу int
, а ось q == 5
- це булеве вираз, і воно дорівнює false , оскільки вище ми проініціалізували q
числом 2. Те ж саме і зі змінною c
. Це число дорівнює 25, а ось (c < 100) - булевий вираз, що дорівнює true . Результат роботи цієї програми:
true
Складні логічні вирази можна застосовувати для перевірки дуже заплутаних і гіллястих умов, проте не варто ними зловживати: вони ускладнюють читання коду.
Порозрядні (побітові) оператори
На початку статті ми згадали, що оператори&
, |
і ^
можна використовувати по відношенню до цілих типів Java. У разі вони є порозрядними операторами. Їх також називають побітовими, оскільки один розряд — це один біт, а ці операції працюють саме з бітами. Зрозуміло, працюють вони дещо по-іншому, ніж логічні оператори, і щоб розуміти, як саме, потрібно знати, що таке двійкова (бінарна) система числення. Якщо ви нічого про неї не знаєте або зовсім забули, пропонуємо для початку ознайомитись зі статтею Java: біти та байти, А решті нагадаємо, що в двійковій системі числення є всього дві цифри - 0 і 1, і всі дані в комп'ютері представлені саме за допомогою умовних нуліків і одиниць. Будь-яке зі звичних нам чисел (десяткових; їм є 10 різних цифр від 0 до 9, з допомогою яких записуємо будь-які числа) представимо в двійковій системі числення. Перевести десяткове число в двійкове можна за допомогою послідовного поділу на стовпчик на основу системи числення (2). Залишки від розподілу на кожному кроці, записані у зворотному порядку, і дадуть нам двійкове число. Ось, наприклад, переведення десяткового числа 103 у двійкову виставу:
Двійкова система числення в курсі JavaRush У курсі JavaRush про двійкову систему числення розповідають під час вивчення квесту MultiThreading (10 рівень, 1 лекція), після лекції є кілька завдань на закріплення. Однак ця тема зовсім не складна, і навіть якщо ви ще не пройшли курсом так далеко, швидше за все, ви в ній розберетеся. |
&
, |
і ^
Java також використовуються порозрядні оператори:
~
порозрядний оператор заперечення>>
побітове зрушення вправо>>>
беззнакове побитове зрушення вправо<<
побітове зрушення вліво
Порозрядні оператори і | і ^
Давайте розглянемо з прикладу, як працюють ці оператори. Допустимо у нас є два цілих числа:int a = 25;
int b = 112;
Нам потрібно застосувати до них три операції &
, |
та ^
й вивести на екран результат. Ось код програми:
public class Solution {
public static void main(String[] args) {
int a = 25;
int b = 112;
int res1 = a & b;
int res2 = a | b;
int res3 = a ^ b;
System.out.println("a & b = " + res1);
System.out.println("a | b = " + res2);
System.out.println("a ^ b = " + res3);
}
}
Результат роботи програми наступний:
a & b = 16
a | b = 121
a ^ b = 105
Якщо не розуміти, що відбувається, то результат виглядає дуже загадковим. Насправді все простіше, ніж здається. Порозрядні оператори "бачать" числа-операнди в їх двійковій формі. І потім застосовують логічні оператори &
або |
до ^
відповідних один одному розрядам (бітам) обох чисел. Так, на &
останній біт двійкового уявлення числа 25 логічно складається з останнім бітом двійкового уявлення числа 112, передостанній - з передостаннім, і так далі: Та ж логіка простежується у випадку з |
і ^
.
Побітове зрушення вліво або вправо
У Java існує кілька операторів побітового зсуву. Найчастіше використовують оператори<<
та >>
. Вони зсувають двійкове уявлення числа відповідно вліво чи вправо, причому у разі зсуву вправо - зі збереженням знака (що означає збереження знака, розповімо трохи нижче). Є ще один оператор зсуву вправо >>>
. Він робить те саме, що й але >>
знак не зберігає. Отже, розглянемо їхню роботу на прикладі. int a = 13
a << 1
зміщує всі біти двійкового уявлення числа a вліво на 1 біт. Для спрощення представимо число 13 у двійковому вигляді як 0000 1101. Насправді це число виглядає так: 00000000 00000000 00000000 00001101, оскільки під числа типуint
Java виділяє 4 байти або 32 біти. Однак у прикладі це ролі не грає, так що в цьому прикладі будемо думати наше число однобайтовим. Звільнений праворуч біт заповнюється нулями. В результаті такої операції ми отримаємо число 26. a << 2
Зміщує всі біти двійкового уявлення числа a
вліво на 2 біти, і два біти, що звільнабося праворуч, заповнюються нулями. В результаті ми отримаємо число 52. a << 3
Видасть результат 104… Помітабо закономірність? Побітовий зсув a
ліворуч на n позицій працює як множення числа a
на 2 ступенем n. Це саме стосується і негативних чисел. Так -13 << 3
дасть результат -104. a >> n
зміщує двійкове уявлення число на n позицій праворуч. Наприклад, 13 >> 1
Перетворює число 1101 на число 0110, тобто, 6. А13 >> 2
дасть у результаті 3. Тобто, по суті, тут ми ділимо число на 2 у ступені n, де n - кількість зрушень вправо, але з одним нюансом: якщо число непарне, ми при цій операції обнулюємо останній біт числа. А ось із негативними справа йде дещо інакше. Скажімо, спробуйте перевірити, що програма видасть, якщо ви попросите її виконати операцію -13 >> 1
. Ви побачите число –7, а не –6, як можна було б подумати. Так відбувається через особливості зберігання негативних чисел Java та інших мовах програмування. Вони зберігаються у так званому додатковому коді. У цьому старший розряд (той, що зліва) віддається під знак. У разі негативного числа старший розряд дорівнює 1.
Додатковий код
Розглянемо числоint a = 13
. Якщо в програмі ви виведемо його двійкове подання в консоль допомогою команди System.out.println(Integer.toBinaryString(a));
, то ми отримаємо 1101. Насправді це скорочений запис, оскільки число типу int
займає в пам'яті 4 байти, тому комп'ютер "бачить" його, швидше за так:
00000000 00000000 00000000 00001101
Старший розряд дорівнює нулю, отже, маємо позитивне число. Для перекладу додатковий код:
-
Записуємо число -13 у так званому "прямому коді". Для цього міняємо старший розряд числа на 1.
Результат дії:10000000 0000000 0000000 00001101
-
Далі інвертуємо всі розряди (змінюємо 0 на 1, а 1 на 0) крім знакового розряду. Його, насправді, ми вже змінабо.
Результат дії:11111111 11111111 11111111 11110010
(так, кроки 1 і 2 можна було б поєднати, але краще уявляти саме так)
- Додаємо до числа 1, що вийшов.
Результат дії:11111111 11111111 11111111 11110011
-13 >> 1
. Оскільки наш оператор >>
зберігає знак, то в цій операції всі біти, що звільнабося зліва, заповнюються не нулями, а одиницями. Таким чином зрушуючи число
11111111 11111111 11111111 11110011
на один біт праворуч, в результаті ми отримаємо наступну послідовність біт:
11111111 11111111 11111111 11111001
Якщо перевести це число в прямий код (тобто спочатку відібрати 1, потім інвертувати всі біти, крім першого) ми отримаємо число:
10000000 00000000 00000000 00000111
або -7. Тепер, коли ми розібралися з оператором зсуву вправо із збереженням знака, стане зрозуміло, у чому його відмінність від оператора >>>
. a >>> n
— ця операція є беззнаковим зрушенням, тобто вона зрушує двійкове уявлення числа a
вправо на n розрядів, але зліва n розрядів, що звільнабося, заповнює не одиницями, як оператор >>
, а нулями. Зробимо операцію -13 >>> 1
. У нас вже є число -13
у додатковому коді:
11111111 11111111 11111111 11110011
При зрушенні вправо на 1 біт і заповненні біт нулем, що звільнився, ми отримуємо наступне число:
01111111 11111111 11111111 11111001
Що у десятковому поданні дає число 2147483641
.
Побітовий оператор заперечення ~
Цей унарний оператор працює дуже просто: він змінює кожен біт бінарного уявлення цілого числа на протилежний. Візьмемо число-13
:
11111111 11111111 11111111 11110011
Операція побитового заперечення ~13
просто змінить значення кожного біта протилежне. В результаті ми отримаємо:
00000000 00000000 00000000 00001100
Або 12
у десятковому вигляді.
Короткі висновки
- Всі логічні оператори застосовуються до булевських виразів, тобто таких, про які можна сказати, true вони або false .
- Якщо оператори
&
,|
або^
застосовуються до числам, йдеться вже не про логічні операції, а про побітові. Тобто обидва числа переводяться в двійкову систему і до цих цифр побитово застосовують операції логічного складання, множення чи віднімання. - У математичній логіці операторам
&
і|
відповідають кон'юнкція та диз'юнкція. - Логічне І схоже на множення 1 ( true ) та 0 ( false ).
- Логічне АБО нагадує пошук максимуму серед 1 ( true ) та 0 ( false ).
- Для побитового заперечення цілого числа a використовується операція
~a
. - Для логічного заперечення булевського виразу a використовується операція
!a
. - Негативні числа зберігаються та обробляються у додатковому коді.
- Порозрядний зсув праворуч може зберігати знак (
>>
), а може – не зберігати (>>>
).
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ