Код
#База знаний

Логические операторы в Java

Знакомимся с каждым, узнаём про короткую и полную схемы вычислений. Проводим побитовые операции с целыми числами. Всё закрепляем на примерах.

Логические операции в Java возвращают значение типа boolean: true или false («правда» или «ложь»). Подробнее о булевом типе мы говорили здесь.

В языке Java есть шесть логических операторов. Первые четыре представлены в таблице ниже.

Логический операторОбозначение в JavaВыражениеРезультат
«И» (AND): конъюнкция, логическое умножение&&true && true

false && false

true && false

false && true
true

false

false

false
Включающее «ИЛИ» (OR): дизъюнкция, логическое сложение||true || true

false || false

true || false

false || true
true

false

true

true
Исключающее «ИЛИ» (XOR): строгая дизъюнкция, логическое вычитание^true ^ true

false ^ false

true ^ false

false ^ true
false

false

true

true
«НЕ» (NOT): инверсия, отрицание!!true

!false
false

true

Где нужны логические операторы

Если коротко, то в условных выражениях, которые могут включать в себя и операторы сравнения (<, >, <=, >=, ==, !=). При вычислении они возвращают значение булева типа.

Условные выражения, в свою очередь, применяются в операторах ветвления (if-else, switch, тернарном). Подробнее об этих операторах тут.

Как применять

Допустим, мы хотим проверить, что значение переменной a больше значений в переменных b и c. То есть сравнить операнд a с двумя другими. Нам поможет логический оператор && (И).

Логический оператор && (И) возвращает true, если слева и справа от него стоят значения true, а иначе — false.

Иными словами, если оба логических высказывания истинны, то и операция && (И) возвращает истину.

Первый пример

int a = 6;
int b = 3;
int c = 4;
boolean d = (a > b) && (a > c);
System.out.println(d);

--OUTPUT> true

Как вычисляется значение выражения (a > b) && (a > c):

Сначала проверяется условие (a > b). Оно вернёт true, так как 6 больше 4. Далее проверяется условие (a > c), которое также вернёт true, ведь 6 больше 3.

Теперь у нас с двух сторон от логического оператора && стоят значения true.

По определению выше или по таблице ещё выше, результат вычисления логического выражения (true && true) равен true.

Второй пример

int a = 6;
int b = 4;
int c = 7;
boolean d = (a > b) && (a > c);
System.out.println(d);

--OUTPUT> false

Результат операции (a > b) вернёт true, так как 6 больше 4, а операция (a > c) уже вернёт false, так как 6 не больше 7.

Значит, слева от логического оператора && стоит true, а справа — false. Следовательно, результат вычисления логического выражения (мы присвоили его булевой переменной d) будет false.

Третий пример

int a = 4;
int b = 5;
int c = 6;
boolean d = (a > b) && (a > c);
System.out.println(d);

--OUTPUT> false

Результат операции сравнения (a > b) равен false, а что вернёт операция (a > c), уже значения не имеет (смотрите определение выше) — результат вычисления логического выражения (мы присвоили его булевой переменной d) будет равен false.

Рассмотрим примеры с другими операторами.

OR

int a = 4;
int b = 6;
int c = 3;
boolean d = (a > b) || (a > c);
System.out.println(d);

--OUTPUT> true

Порядок вычисления:

  • (a > b) || (a > c)
  • (4 > 6) || (4 > 3)
  • false || (4 > 3)
  • false || true
  • true

Значение переменной d равно true.

int a = 9;
int b = 9;
boolean c = a > b || a != b;
System.out.println(c);

--OUTPUT> false

Теперь вычисляйте вы.

XOR

int a = 5;
int b = 6;
int c = 7;
boolean d = (b > a) ^ (c > a);
System.out.println(d);

--OUTPUT> false

Порядок вычисления:

  • (b > a) ^ (c > a)
  • (6 > 5) ^ (7 > 5)
  • true ^ (7 > 5)
  • true ^ true
  • false

Значение d равно false.

NOT

int a = 5;
int b = 9;
boolean d = !(a > b);
System.out.println(d);

--OUTPUT> true

Порядок вычисления:

  • !(a > b)
  • !(5 > 9)
  • !false
  • true

Значение d стало true.

Полные и сокращённые версии AND и OR

&& и || называются сокращёнными логическими операторами AND и OR соответственно, или операторами короткой схемы вычислений. В спецификации Java их ещё зовут условными. Значения их операндов могут быть только булева типа.

В отличие от двойных, одиночные & и | называются операторами полной схемы вычислений. Значения их операндов могут быть как только булевыми, так и только целочисленными (вместе с оператором ^ они используются в побитовых операциях).

В чём разница

В том, что для операторов & и | всегда вычисляются значения обоих операндов, а при работе операторов && и || второй операнд вычисляется только по необходимости.

То есть иногда результат выражения однозначно определён уже по первому операнду:

  • Если первый операнд && равен false, то второй не вычисляется, так как уже понятно, что результат всего выражения будет false.
  • Если первый операнд || равен true, то второй не вычисляется, так как уже понятно, что || вернёт true.

&& и || используют как операторы булевой логики. Они оперируют значениями только булева типа и применяются только в логических выражениях.

Как использовать

&& и || позволяют экономить вычисления (применять короткую схему) и помогают избегать ошибок. Как это делается?

Начнём с оператора &&. Приведём фрагмент из таблицы выше:

Логический операторОбозначение в JavaВыражениеРезультат
«И» (AND): конъюнкция, логическое умножение&&true && true

false && false

true && false

false && true
true

false

false

false

Рассмотрим выражение: (3 > 4) AND (5 > 4)

Мы видим, что операнд слева от оператора AND равен false. Смотрим на таблицу выше — и понимаем, что вычислять второй операнд бессмысленно, так как оператор AND уже точно вернёт false.

Именно по такой логике и работает оператор короткой схемы вычислений &&. Если выражение слева от него равно false, то выражение справа вычисляться не будет.

Так же и с оператором ||: если выражение слева от него равно true, то выражение справа не вычисляется, так как результат операции || всё равно будет true.

В большинстве случае применяют именно && и ||. При верном использовании они избавляют Java от ненужных вычислений и страхуют от некоторых ошибок.

Первый пример

int a = 3;
int b = 0;

boolean d = (b != 0) && (a/b > 0);
System.out.println(d);

--OUTPUT> false

Если вместо оператора && мы используем &, то получим ошибку (исключение) java.lang.ArithmeticException: / by zero:

int a = 3;
int b = 0;

boolean d = (b != 0) & (a/b > 0);
System.out.println(d);

--OUTPUT> Exception in thread <...> java.lang.ArithmeticException: / by zero
	at <...>

Ошибка возникнет тогда, когда Java попытается вычислить второй аргумент логического выражения, если первый равнялся false.

Иными словами, мы узнали, что b равно 0 (выражение b != 0 вернуло false) — и идём делить на b (делить на ноль), вычисляя значение второго операнда (a/b > 0).

Второй пример

public void printStringLengthMoreThenZero(String str) {
   if (str != null && str.length() > 0) {
       System.out.println(str.length());
   } else {
       System.out.println("Тут нечего считать!");
   }
}

Код выше выводит в консоль длину строки str, в которой есть хотя бы один символ. А если строка пуста или её значение равно null (то есть строковая переменная ни на что не указывает), в консоль выводится сообщение: «Тут нечего считать!»

Мы выбрали оператор короткой схемы вычислений && — и это правильно!

А вот если бы вместо этого использовали оператор полной схемы &, то наш код работал бы не так, как надо.

Мы получали бы ошибку NullPointerException каждый раз, когда вызываем метод для строковой переменной со значением null.

Посмотрим, что происходило бы при вычислении условия блока if:

  • str != null & str.length() > 0
  • null != null & str.length() > 0
  • false & str.length() > 0 // тут возникает ошибка

Сперва вычисляется первый аргумент логического выражения, а именно str != null (иными словами, получаем ответ на вопрос «Строковая переменная не равна null?»). Получили false, значит всё же равна.

Дальше Java должна вычислить второй аргумент логического выражения, а именно str.length() > 0 (иными словами — проверяется «Число символов строки > 0?»).

Для этого вызывается метод str.length(), который должен вернуть целое значение. Оно и будет сравниваться с 0. Но у нас-то str равна null (возвращать методу нечего, строки нет). Тут Java и пожалуется на NullPointerException.

Порядок выполнения операторов

Когда в выражении несколько логических операторов, результат вычисляется с учётом их приоритета. Если нет логических скобок, то операции выполняются в таком порядке:

  • ! (NOT)
  • & (AND)
  • ^ (XOR)
  • | (OR)
  • && (условный AND)
  • || (условный OR)

Если одинаковые операции стоят по соседству, то раньше выполняется та, что левее.

Первый пример

boolean a = true ^ true & false;
System.out.println(a);

Вычислим true ^ true & false:

  • Выбираем самый приоритетный оператор (если таких больше одного — тот, что левее). У нас самый приоритетный & (он здесь такой один).
  • Смотрим, что слева и справа от него: это true и false соответственно.
  • Вычисляем выражение true & false — получаем false.
  • В исходном выражении заменяем true & false результатом его вычисления (false) — и получаем: true ^ false.
  • Вычислив это выражение, получаем результат true.

Или короче:

  • true ^ true & false
  • true ^ false
  • true

Второй пример

Заменим & на &&:

boolean a = true ^ true && false;
System.out.println(a);

Теперь самый приоритетный оператор в выражении это ^ — и порядок вычислений будет уже другой:

  • true ^ true && false
  • false && false
  • false

Результат будет false.

Как изменить порядок вычисления

Порядок вычисления логических операторов меняют круглые скобки — так же, как в арифметике:

boolean a = (true ^ true) & false;
System.out.println(a);

--OUTPUT> false

Добавив круглые скобки, мы поменяли приоритеты для вычисления. Теперь сперва будет определено выражение (true ^ true), которое вернёт false. А после — вычислится выражение false & false, которое тоже вернёт false.

То есть скобки повышают приоритет стоящего внутри выражения, а внутри самих скобок действуют прежние приоритеты.

Пример посложнее — выражение !(true && (false || true)) ^ !false.

Порядок вычисления:

  • !(true && (false || true)) ^ !false
  • !(true && true) ^ !false
  • !true ^ !false
  • false ^ !false
  • false ^ true
  • true

Результат: true.

Как логические операторы работают с целыми числами

Мы уже знаем, что логические операции применимы к логическим аргументам (операндам). Каждый логический операнд — это выражение, которое является истинным (true) или ложным (false) — то есть возвращает булево значение. Иными словами, логический операнд — это выражение типа boolean.

Выходит, применять логические операторы к целочисленным аргументам нельзя?

Можно. Внутри Java все целочисленные типы представлены двоичными числами разной длины. И к ним уже применимы бинарные логические операторы ^, | и &.

Только в этом случае они работают с двоичным представлением операндов — выполняют операции над их битами попарно (рассматривая их как логические единицы и нули). Поэтому и сами операторы ^, | и & зовутся побитовыми.

Как ^, | и & работают с целочисленными операндами

Рассмотрим пример:

int a = 3 & 5;
int b = 3 | 5;
int c = 3 ^ 5;
System.out.println(a);
System.out.println(b);
System.out.println(c);

--OUTPUT> 1
--OUTPUT> 7
--OUTPUT> 6

Чтобы повторить вычисления Java, нужно:

  • Перевести значения обоих операндов в двоичную систему счисления.
  • Расположить результаты перевода друг под другом.
  • Сравнять в них число разрядов (дополнить лидирующими нулями).
  • Применить к битам из каждого столбца оператор (&, | или ^).
  • Записать результат каждой операции ниже в том же столбце.
  • Перевести итог в десятичную форму.

Потренируемся: вычислим сами 3 & 5

Число 3 в двоичной системе счисления имеет вид 11, а число 5 — 101.

Так как у числа 5 три разряда в двоичной системе, а у числа 3 — всего два, добавим лидирующий ноль к числу 3 в двоичной системе и получим 011.

Берём цифры из обоих чисел и применяем к ним попарно оператор & (AND):

3(10) = 011(2)011
&&&
5(10) = 101(2)101
===
001(2) = 1(10)001

Получаем число 001. В десятичной записи ему соответствует число 1. Поэтому операция 3 & 5 и возвращает в результате 1.

Вычислим 3 | 5

С оператором | действуем так же:

3(10)  = 011(2)011
|||
5(10) = 101(2)101
===
111(2) = 7(10)111

Рассчитаем 3 ^ 5

3(10)  = 011(2)011
^^^
5(10) = 101(2)101
===
110(2) = 6(10)110

Что дальше?

Сперва подытожим:

  • мы познакомились с логическими операторами в Java;
  • научились вычислять условные выражения с ними;
  • разобрались, как они работают с целыми числами.

В одной из статей мы говорили про операторы сравнения <, >, <=, >=, ==, !=, а также instanceof, про условные конструкции if-else и switch. Учились работать с тернарным оператором.

Если пропустили — лучше вернитесь и прочтите.

Изучайте IT на практике — бесплатно

Курсы за 2990 0 р.

Я не знаю, с чего начать
Научитесь: Профессия Java-разработчик PRO Узнать больше
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована