Последнее обновление: 30.10.2018
Побитовые или поразрядные операции выполняются над отдельными разрядами или битами чисел. В данных операциях в качестве операндов могу выступать только целые числа.
Каждое число имеет определенное двоичное представление. Например, число 4 в двоичной системе 100, а число 5 - 101 и так далее.
К примеру, возьмем следующие переменны:
Byte b = 7; // 0000 0111 short s = 7; // 0000 0000 0000 0111
Тип byte занимает 1 байт или 8 битов, соответственно представлен 8 разрядами. Поэтому значение переменной b в двоичном коде будет равно 00000111 . Тип short занимает в памяти 2 байта или 16 битов, поэтому число данного типа будет представлено 16 разрядами. И в данном случае переменная s в двоичной системе будет иметь значение 0000 0000 0000 0111 .
Для записи чисел со знаком в Java применяется дополнительный код (two’s complement), при котором старший разряд является знаковым. Если его значение равно 0, то число положительное, и его двоичное представление не отличается от представления беззнакового числа. Например, 0000 0001 в десятичной системе 1.
Если старший разряд равен 1, то мы имеем дело с отрицательным числом. Например, 1111 1111 в десятичной системе представляет -1. Соответственно, 1111 0011 представляет -13.
Логические операции над числами представляют поразрядные операции. В данном случае числа рассматриваются в двоичном представлении, например, 2 в двоичной системе равно 10 и имеет два разряда, число 7 - 111 и имеет три разряда.
& (логическое умножение)
Умножение производится поразрядно, и если у обоих операндов значения разрядов равно 1, то операция возвращает 1, иначе возвращается число 0. Например:
Int a1 = 2; //010 int b1 = 5;//101 System.out.println(a1&b1); // результат 0 int a2 = 4; //100 int b2 = 5; //101 System.out.println(a2 & b2); // результат 4
В первом случае у нас два числа 2 и 5. 2 в двоичном виде представляет число 010, а 5 - 101. Поразрядное умножение чисел (0*1, 1*0, 0*1) дает результат 000.
Во втором случае у нас вместо двойки число 4, у которого в первом разряде 1, так же как и у числа 5, поэтому здесь результатом операции (1*1, 0*0, 0 *1) = 100 будет число 4 в десятичном формате.
| (логическое сложение)
Данная операция также производится по двоичным разрядам, но теперь возвращается единица, если хотя бы у одного числа в данном разряде имеется единица (операция "логическое ИЛИ"). Например:
Int a1 = 2; //010 int b1 = 5;//101 System.out.println(a1|b1); // результат 7 - 111 int a2 = 4; //100 int b2 = 5;//101 System.out.println(a2 | b2); // результат 5 - 101
^ (логическое исключающее ИЛИ)
Также эту операцию называют XOR, нередко ее применяют для простого шифрования:
Int number = 45; // 1001 Значение, которое надо зашифровать - в двоичной форме 101101 int key = 102; //Ключ шифрования - в двоичной системе 1100110 int encrypt = number ^ key; //Результатом будет число 1001011 или 75 System.out.println("Зашифрованное число: " +encrypt); int decrypt = encrypt ^ key; // Результатом будет исходное число 45 System.out.println("Расшифрованное число: " + decrypt);
Здесь также производятся поразрядные операции. Если у нас значения текущего разряда у обоих чисел разные, то возвращается 1, иначе возвращается 0. Например, результатом выражения 9^5 будет число 12. А чтобы расшифровать число, мы применяем обратную операцию к результату.
~ (логическое отрицание)
Поразрядная операция, которая инвертирует все разряды числа: если значение разряда равно 1, то оно становится равным нулю, и наоборот.
Byte a = 12; // 0000 1100 System.out.println(~a); // 1111 0011 или -13
Операции сдвига также производятся над разрядами чисел. Сдвиг может происходить вправо и влево.
a<
a>>b - смещает число a вправо на b разрядов. Например, 16>>1 сдвигает число 16 (которое в двоичной системе 10000) на один разряд вправо, то есть в итоге получается 1000 или число 8 в десятичном представлении.
a>>>b - в отличие от предыдущих типов сдвигов данная операция представляет беззнаковый сдвиг - сдвигает число a вправо на b разрядов. Например, выражение -8>>>2 будет равно 1073741822.
Таким образом, если исходное число, которое надо сдвинуть в ту или другую строну, делится на два, то фактически получается умножение или деление на два. Поэтому подобную операцию можно использовать вместо непосредственного умножения или деления на два, так как операция сдвига на аппаратном уровне менее дорогостоящая операция в отличие от операции деления или умножения.
Тебе наверняка знакомо слово “бит”. Если же нет, давай познакомимся с ним:) Бит - минимальная единица измерения информации в компьютере. Его название происходит от английского “binary digit ” - “двоичное число”. Бит может быть выражен одним из двух чисел: 1 или 0. Существует специальная система счисления, основанная на единицах и нулях - двоичная .
Не будем углубляться в дебри математики и отметим лишь, что любое число в Java можно сконвертировать в его двоичную форму. Для этого нужно использовать классы-обертки. Например, вот как можно сделать это для числа int: public class Main { public static void main (String args) { int x = 342 ; System. out. println (Integer. toBinaryString (x) ) ; } } Вывод в консоль: 101010110 1010 10110 (я добавил пробел для удобства чтения) - это число 342 в двоичной системе. Мы фактически разделили это число на отдельные биты - нули и единицы. Именно с ними мы можем выполнять операции, которые называются побитовыми.
~ - побитовый оператор “НЕ”.
& - побитовый оператор “И”
А теперь мы, в прямом смысле слова, берем каждый из наших битов и сдвигаем влево на 3 ячейки:
Вот что у нас получилось. Как видишь, все наши биты сдвинулись, а из-за пределов диапазона добавились еще 3 нуля. 3 - потому что мы делали сдвиг на 3. Если бы мы сдвигали на 10, добавилось бы 10 нулей. Таким образом, выражение x << y означает “сдвинуть биты числа х на y ячеек влево”. Результатом нашего выражения стало число 1000000000, которое в десятичной системе равно 512. Проверим: public class Main { public static void main (String args) { int x = 64 ; //значение int y = 3 ; //количество int z = (x << y) ; System. out. println (z) ; } } Вывод в консоль: 512 Все верно! Теоретически, биты можно сдвигать до бесконечности. Но поскольку у нас число int , в распоряжении есть всего 32 ячейки. Из них 7 уже заняты числом 64 (1000000). Поэтому если мы сделаем, например, 27 сдвигов влево, наша единственная единица выйдет за пределы диапазона и “затрётся”. Останутся только нули! public class Main { public static void main (String args) { int x = 64 ; //значение int y = 26 ; //количество int z = (x << y) ; System. out. println (z) ; } } Вывод в консоль: 0 Как мы и предполагали, единичка вышла за пределы 32 ячеек-битов и исчезла. У нас получилось 32-битное число, состоящее из одних нулей.
Естественно, в десятичной системе ему соответствует 0. Простое правило для запоминания сдвигов влево: При каждом сдвиге влево выполняется умножение числа на 2. Например, попробуем без картинок с битами посчитать результат выражения 111111111 << 3 Нам нужно трижды умножить число 111111111 на 2. В результате у нас получается 888888888. Давай напишем код и проверим: public class Main { public static void main (String args) { System. out. println (111111111 << 3 ) ; } } Вывод в консоль: 888888888
В результате сдвига на 2 вправо два крайних нуля нашего числа вышли за пределы диапазона и затерлись. У нас получилось число 10000, которому в десятичной системе соответствует число 16 Вывод в консоль: 16 Простое правило для запоминания сдвигов вправо: При каждом сдвиге вправо выполняется деление на два с отбрасыванием любого остатка. Например, 35 >> 2 означает, что нам нужно 2 раза разделить 35 на 2, отбрасывая остатки 35/2 = 17 (отбросили остаток 1) 17:2 = 8 (отбросили остаток 1) Итого, 35 >> 2 должно быть равно 8. Проверяем: public class Main { public static void main (String args) { System. out. println (35 >> 2 ) ; } } Вывод в консоль: 8
Operator Precedence
Operators | Precedence |
---|---|
postfix | expr++ expr-- |
unary | ++expr --expr +expr ~ ! |
Multiplicative | * / % |
additive | + - |
shift | << >> >>> |
relational | < > <= >= instanceof |
equality | == != |
bitwise AND | & |
bitwise exclusive OR | ^ |
bitwise inclusive OR | | |
logical AND | && |
logical OR | || |
ternary | ? : |
assignment | = += -= *= /= %= &= ^= |= <<= >>= >>>= |
12*12 = 144
4 > 3 = true
144 <= 119 = false
И, наконец, последним, будет выполнен оператор “И” && .
boolean x = true && false;
boolean x = false;
Оператор сложения (+), например, имеет более высокий приоритет, чем оператор сравнения!= (“не равно”);
Поэтому в выражении:
Boolean x = 7 != 6+1;
сначала будет выполнена операция 6+1, потом проверка 7!=7 (false), а в конце - присваивания результата false переменной x . У присваивания вообще самый маленький приоритет из всех операций - посмотри в таблице.
Последнее обновление: 30.10.2018
Большинство операций в Java аналогичны тем, которые применяются в других си-подобных языках. Есть унарные операции (выполняются над одним операндом), бинарные - над двумя операндами, а также тернарные - выполняются над тремя операндами. Операндом является переменная или значение (например, число), участвующее в операции. Рассмотрим все виды операций.
В арифметических операциях участвуют числами. В Java есть бинарные арифметические операции (производятся над двумя операндами) и унарные (выполняются над одним операндом). К бинарным операциям относят следующие:
операция сложения двух чисел:
Int a = 10; int b = 7; int c = a + b; // 17 int d = 4 + b; // 11
операция вычитания двух чисел:
Int a = 10; int b = 7; int c = a - b; // 3 int d = 4 - a; // -6
операция умножения двух чисел
Int a = 10; int b = 7; int c = a * b; // 70 int d = b * 5; // 35
операция деления двух чисел:
Int a = 20; int b = 5; int c = a / b; // 4 double d = 22.5 / 4.5; // 5.0
При делении стоит учитывать, так как если в операции участвуют два целых числа, то результат деления будет округляться до целого числа, даже если результат присваивается переменной float или double:
Double k = 10 / 4; // 2 System.out.println(k);
Чтобы результат представлял числос плавающей точкой, один из операндов также должен представлять число с плавающей точкой:
Double k = 10.0 / 4; // 2.5 System.out.println(k);
получение остатка от деления двух чисел:
Int a = 33; int b = 5; int c = a % b; // 3 int d = 22 % 4; // 2 (22 - 4*5 = 2)
Также есть две унарные арифметические операции, которые производятся над одним числом: ++ (инкремент) и -- (декремент). Каждая из операций имеет две разновидности: префиксная и постфиксная:
++ (префиксный инкремент)
Предполагает увеличение переменной на единицу, например, z=++y (вначале значение переменной y увеличивается на 1, а затем ее значение присваивается переменной z)
Int a = 8; int b = ++a; System.out.println(a); // 9 System.out.println(b); // 9
++ (постфиксный инкремент)
Также представляет увеличение переменной на единицу, например, z=y++ (вначале значение переменной y присваивается переменной z, а потом значение переменной y увеличивается на 1)
Int a = 8; int b = a++; System.out.println(a); // 9 System.out.println(b); // 8
-- (префиксный декремент)
уменьшение переменной на единицу, например, z=--y (вначале значение переменной y уменьшается на 1, а потом ее значение присваивается переменной z)
Int a = 8; int b = --a; System.out.println(a); // 7 System.out.println(b); // 7
-- (постфиксный декремент)
z=y-- (сначала значение переменной y присваивается переменной z, а затем значение переменной y уменьшается на 1)
Int a = 8; int b = a--; System.out.println(a); // 7 System.out.println(b); // 8
Одни операции имеют больший приоритет чем другие и поэтому выполняются вначале. Операции в порядке уменьшения приоритета:
++ (инкремент), -- (декремент)
* (умножение), / (деление), % (остаток от деления)
+ (сложение), - (вычитание)
Приоритет операций следует учитывать при выполнении набора арифметических выражений:
Int a = 8; int b = 7; int c = a + 5 * ++b; System.out.println(c); // 48
Вначале будет выполняться операция инкремента ++b , которая имеет больший приоритет - она увеличит значение переменной b и возвратит его в качестве результата. Затем выполняется умножение 5 * ++b , и только в последнюю очередь выполняется сложение a + 5 * ++b
Скобки позволяют переопределить порядок вычислений:
Int a = 8; int b = 7; int c = (a + 5) * ++b; System.out.println(c); // 104
Несмотря на то, что операция сложения имеет меньший приоритет, но вначале будет выполняться именно сложение, а не умножение, так как операция сложения заключена в скобки.
Кроме приоритета операции отличаются таким понятием как ассоциативность . Когда операции имеют один и тот же приоритет, порядок вычисления определяется ассоциативностью операторов. В зависимости от ассоциативности есть два типа операторов:
Левоассоциативные операторы, которые выполняются слева направо
Правоассоциативные операторы, которые выполняются справа налево
Так, некоторые операции, например, операции умножения и деления, имеют один и тот же приоритет. Какой же тогда будет результат в выражении:
Int x = 10 / 5 * 2;
Стоит нам трактовать это выражение как (10 / 5) * 2 или как 10 / (5 * 2) ? Ведь в зависимости от трактовки мы получим разные результаты.
Поскольку все арифметические операторы (кроме префиксного инкремента и декремента) являются левоассоциативными, то есть выполняются слева направо. Поэтому выражение 10 / 5 * 2 необходимо трактовать как (10 / 5) * 2 , то есть результатом будет 4.
Следует отметить, что числа с плавающей точкой не подходят для финансовых и других вычислений, где ошибки при округлении могут быть критичными. Например:
Double d = 2.0 - 1.1; System.out.println(d);
В данном случае переменная d будет равна не 0.9, как можно было бы изначально предположить, а 0.8999999999999999. Подобные ошибки точности возникают из-за того, что на низком уровне для представления чисел с плавающей точкой применяется двоичная система, однако для числа 0.1 не существует двоичного представления, также как и для других дробных значений. Поэтому если в таких случаях обычно применяется класс BigDecimal, который позволяет обойти подобные сиуации.
Операторы в языке Java - это специальные символы, которые сообщают транслятору о том, что вы хотите выполнить операцию с некоторыми операндами. Некоторые операторы требуют одного операнда, их называют унарными. Одни операторы ставятся перед операндами и называются префиксными, другие - после, их называют постфиксными операторами. Большинство же операторов ставят между двумя операндами, такие операторы называются инфиксными бинарными операторами. Существует тернарный оператор, работающий с тремя операндами.
В Java имеется 44 встроенных оператора. Их можно разбить на 4 класса - арифметические, битовые, операторы сравнения и логические.
Арифметические операторы
Арифметические операторы используются для вычислений так же как в алгебре (см. таблицу со сводкой арифметических операторов ниже). Допустимые операнды должны иметь числовые типы. Например, использовать эти операторы для работы с логическими типами нельзя, а для работы с типом char можно, поскольку в Java тип char - это подмножество типа int.
Оператор |
Результат |
Оператор |
Результат |
Сложение |
сложение с присваиванием |
||
вычитание (также унарный минус) |
вычитание с присваиванием |
||
Умножение |
умножение с присваиванием |
||
деление с присваиванием |
|||
деление по модулю |
деление по модулю с присваиванием |
||
Инкремент |
декремент |
Четыре арифметических действия
Ниже, в качестве примера, приведена простая программа, демонстрирующая использование операторов. Обратите внимание на то, что операторы работают как с целыми литералами, так и с переменными.
class BasicMath{ public static void int a = 1 + 1;
intb = a *3;
main(String args) {
int c = b / 4;
int d = b - а;
int e = -d;
System.out.println("a =" +а);
System.out.println("b =" +b);
System.out.println("c =" +c);
System.out.println("d =" +d);
System.out.println("e =" +e);
} }
Исполнив эту программу, вы должны получить приведенный ниже результат:
C: \> java BasicMath
a = 2
b = 6
c = 1
d = 4
e = -4
Оператор деления по модулю
Оператор деления по модулю, или оператор mod, обозначается символом %. Этот оператор возвращает остаток от деления первого операнда на второй. В отличие от C++, функция mod в Java работает не только с целыми, но и с вещественными типами. Приведенная ниже программа иллюстрирует работу этого оператора.
class Modulus {
public static void main (String args ) {
int x = 42;
double у = 42.3;
System.out.println("x mod 10 = " + x % 10);
System.out.println("y mod 10 = " + у % 10);
} }
Выполнив эту программу, вы получите следующий результат:
С:\> Modulus
x mod 10 = 2
y mod 10 = 2.3
Арифметические операторы присваивания
Для каждого из арифметических операторов есть форма, в которой одновременно с заданной операцией выполняется присваивание. Ниже приведен пример, который иллюстрирует использование подобной разновидности операторов.
class OpEquals {
int a = 1;
int b = 2;
int с = 3;
a += 5;
b *= 4;
c += a * b;
с %= 6;
} }
А вот и результат, полученный при запуске этой программы:
С:> Java OpEquals
а = 6
b = 8
с = 3
Инкремент и декремент
В С существует 2 оператора, называемых операторами инкремента и декремента (++ и --) и являющихся сокращенным вариантом записи для сложения или вычитания из операнда единицы. Эти операторы уникальны в том плане, что могут использоваться как в префиксной, так и в постфиксной форме. Следующий пример иллюстрирует использование операторов инкремента и декреме нта.
class IncDec {
public static void main(String args) {
int a = 1;
int b = 2;
int c = ++b;
int d = a++;
c++;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
} }
Результат выполнения данной программы будет таким:
C:\ java IncDec
a = 2
b = 3
c = 4
d = 1
Для целых числовых типов данных - long, int, short, char и byte, определен дополнительный набор операторов, с помощью которых можно проверять и модифицировать состояние отдельных битов соответствующих значений. В таблице приведена сводка таких операторов. Операторы битовой арифметики работают с каждым битом как с самостоятельной величиной.
Оператор |
Результат |
Оператор |
Результат |
побитовое унарное отрицание (NOT) |
|||
побитовое И (AND) |
побитовое И (AND) с присваиванием |
||
побитовое ИЛИ (OR) |
побитовое ИЛИ (OR) с присваиванием |
||
побитовое исключающее ИЛИ (XOR) |
побитовое исключающее ИЛИ (XOR) с присваиванием |
||
сдвиг вправо |
сдвиг вправо с присваиванием |
||
сдвиг вправо с заполнением нулями |
сдвиг вправо с заполнением нулями с присваиванием |
||
сдвиг влево |
сдвиг влево с присваиванием |
Пример программы, манипулирующей с битами
В таблице, приведенной ниже, показано, как каждый из операторов битовой арифметики воздействует на возможные комбинации битов своих операндов. Приведенный после таблицы пример иллюстрирует использование этих операторов в программе на языке Java.
class Bitlogic {
public static void main(String args ) {
String binary = { "OOOO", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001","1010", "1011", "1100", "1101",
"1110", "1111" };
int a = 3;//0+2+1или двоичное 0011
int b = 6;//4+2+0или двоичное 0110
int c = a | b;
int d = a & b;
int e = a ^ b;
int f = (~a & b) | (a & ~b);
int g = ~a & 0x0f;
System.out.println(" a = " + binary[a]);
System.out.println(" b = " + binary[b]);
System.out.println(" ab = " + binary[c]);
System.out.println(" a&b = " + binary[d]);
System.out.println(" a^b = " + binary[e]);
System.out.рrintln("~a&b|а^~Ь = " + binary[f]);
System.out.println(" ~a = " + binary[g]);
} }
Ниже приведен результат, полученный при выполнении этой программы:
С: \> Java BitLogic
a = 0011
b = 0110
a | b = 0111
a & b = 0010
a ^ b = 0101
~a & b | a & ~b = 0101
~а = 1100
Оператор << выполняет сдвиг влево всех битов своего левого операнда на число позиций, заданное правым операндом. При этом часть битов в левых разрядах выходит за границы и теряется, а соответствующие правые позиции заполняются нулями. В предыдущей главе уже говорилось об автоматическом повышении типа всего выражения до int в том случае если в выражении присутствуют операнды типа int или целых типов меньшего размера. Если же хотя бы один из операндов в выражении имеет тип long, то и тип всего выражения повышается до long.
Оператор >> означает в языке Java сдвиг вправо. Он перемещает все биты своего левого операнда вправо на число позиций, заданное правым операндом.Когда биты левого операнда выдвигаются за самую правую позицию слова, они теряются. При сдвиге вправо освобождающиеся старшие (левые) разряды сдвигаемого числа заполняются предыдущим содержимым знакового разряда. Такое поведение называют расширением знакового разряда.
В следующей программе байтовое значение преобразуется в строку, содержащую его шестнадцатиричное представление. Обратите внимание - сдвинутое значение приходится маскировать, то есть логически умножать на значение 0 х0 f, для того, чтобы очистить заполняемые в результате расширения знака биты и понизить значение до пределов, допустимых при индексировании массива шестнадцатиричных цифр.
classHexByte {
char hex = { "0", "1, "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f };
byte b = (byte) 0xf1;
System.out.println(“b = 0x” + hex[(b >> 4) & 0x0f] + hex);
} }
Ниже приведен результат работы этой программы:
С:\> java HexByte
b = 0xf1
Беззнаковый сдвиг вправо
Часто требуется, чтобы при сдвиге вправо расширение знакового разряда не происходило, а освобождающиеся левые разряды просто заполнялись бы нулями.
class ByteUShift{
static public void main(String args) {
char hex = { "0", "1’, "2", "3", "4","5", "6", "7", "8", "9", "а", "b", "с", "d","e", "f’ };
byte b = (byte) 0xf1;
byte c = (byte) (b >> 4);
byte d = (byte) (b >> 4);
byte e = (byte) ((b & 0xff) >> 4);
System.out.println(" b = 0x" + hex(b >> 4) & 0x0f] + hex);
System.out.println(“ b >> 4 =0x" + hex[(c >> 4) & 0x0f] + hex);
System.out.println(“b >>> 4 = 0x" + hex[(d >> 4) & 0x0f] + hex);
System.out.println(“(b & 0xff) >> 4 = 0x" + hex[(e >> 4) & 0x0f] + hex);
} }
Для этого примера переменную b можно было бы инициализировать произвольным отрицательным числом, мы использовали число с шестнадцатиричным представлением 0xf1. Переменной с присваивается результат знакового сдвига b вправо на 4 разряда. Как и ожидалось, расширение знакового разряда приводит к тому, что 0xf1 превращается в 0xff. Затем в переменную d заносится результат беззнакового сдвига b вправо на 4 разряда. Можно было бы ожидать, что в результате d содержит 0x0f, однако на деле мы снова получаем 0xff. Это - результат расширения знакового разряда, выполненного при автоматическом повышении типа переменной b до int перед операцией сдвига вправо. Наконец, в выражении для переменной е нам удается добиться желаемого результата - значения 0x0f. Для этого нам пришлось перед сдвигом вправо логически умножить значение переменной b на маску 0xff, очистив таким образом старшие разряды, заполненные при автоматическом повышении типа. Обратите внимание, что при этом уже нет необходимости использовать беззнаковый сдвиг вправо, поскольку мы знаем состояние знакового бита после операции AND.
С: \> java ByteUShift
b = 0xf1
b >> 4 = 0xff
b >>> 4 = 0xff
b & 0xff) >> 4 = 0x0f
Так же, как и в случае арифметических операторов, у всех бинарных битовых операторов есть родственная форма, позволяющая автоматически присваивать результат операции левому операнду. В следующем примере создаются несколько целых переменных, с которыми с помощью операторов, указанных выше, выполняются различные операции.
class OpBitEquals {
public static void main(String args) {
int a = 1;
int b = 2;
int с = 3;
a |= 4;
b >>= 1;
с <<= 1;
а ^= с;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
} }
Результаты исполнения программы таковы:
С:\> Java OpBitEquals
а = 3
b = 1
с = 6
Операторы отношения
Для того, чтобы можно было сравнивать два значения, в Java имеется набор операторов, описывающих отношение и равенство. Список таких операторов приведен в таблице.
Оператор |
Результат |
больше или равно |
|
меньше или равно |
Значения любых типов, включая целые и вещественные числа, символы, логические значения и ссылки, можно сравнивать, используя оператор проверки на равенство == и неравенство!=. Обратите внимание - в языке Java, так же, как в С и C++ проверка на равенство обозначается последовательностью (==). Один знак (=) - это оператор присваивания.
Булевы логические операторы
Булевы логические операторы, сводка которых приведена в таблице ниже, оперируют только с операндами типа boolean. Все бинарные логические операторы воспринимают в качестве операндов два значения типа boolean и возвращают результат того же типа.
Оператор |
Результат |
Оператор |
Результат |
логическое И (AND) |
И (AND) с присваиванием |
||
логическое ИЛИ (OR) |
ИЛИ (OR) с присваиванием |
||
логическое исключающее ИЛИ (XOR) |
исключающее ИЛИ (XOR) с присваиванием |
||
оператор OR быстрой оценки выражений (short circuit OR) |
|||
оператор AND быстрой оценки выражений (short circuit AND) |
|||
логическое унарное отрицание (NOT) |
тернарный оператор if-then-else |
Результаты воздействия логических операторов на различные комбинации значений операндов показаны в таблице.
Программа, приведенная ниже, практически полностью повторяет ужезнакомый вам пример BitLogic. Только но на этот раз мы работаем с булевыми логическими значениями.
class BoolLogic {
public static void main(String args) {
boolean a = true;
boolean b = false;
boolean с = a | b;
boolean d = a & b;
boolean e = a ^ b;
boolean f = (!a & b) | (a & !b);
boolean g = !a;
System.out.println(" a = " + a);
System.out.println(" b = " + b);
System.out.println(" a|b = " + c);
System.out.println(" a&b = " + d);
System.out.println(" a^b = " + e);
System.out.println("!a&b|a&!b = " + f);
System.out.println(" !a = " + g);
} }
С: \> Java BoolLogic
а = true
b = false
a|b = true
a&b = false
a^b = true
!a&b|a&!b = true
!a = false
Операторы быстрой оценки логических выражений (short circuit logical operators)
Существуют два интересных дополнения к набору логических операторов. Это - альтернативные версии операторов AND и OR, служащие для быстрой оценки логических выражений. Вы знаете, что если первый операнд оператора OR имеет значение true, то независимо от значения второго операнда результатом операции будет величина true. Аналогично в случае оператора AND, если первый операнд - false, то значение второго операнда на результат не влияет - он всегда будет равен false. Если вы в используете операторы && и || вместо обычных форм & и |, то Java не производит оценку правого операнда логического выражения, если ответ ясен из значения левого операнда. Общепринятой практикой является использование операторов && и || практически во всех случаях оценки булевых логических выражений. Версии этих операторов & и | применяются только в битовой арифметике.
Тернарный оператор if-then-else
Общая форма оператора if-then-use такова:
выражение1? выражение2: выражениеЗ
В качестве первого операнда - «выражение1» - может быть использовано любое выражение, результатом которого является значение типа boolean. Если результат равен true, то выполняется оператор, заданный вторым операндом, то есть, «выражение2». Если же первый операнд paвен false, то выполняется третий операнд - «выражениеЗ». Второй и третий операнды, то есть «выражение2» и «выражениеЗ», должны возвращать значения одного типа и не должны иметь тип void.
В приведенной ниже программе этот оператор используется для проверки делителя перед выполнением операции деления. В случае нулевого делителя возвращается значение 0.
class Ternary {
public static void main(String args) {
int a = 42;
int b = 2;
int c = 99;
int d = 0;
int e = (b == 0) ? 0: (a / b);
int f = (d == 0) ? 0: (c / d);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
System.out.println("d = " + d);
System.out.println("a / b = " + e);
System.out.println("c / d = " + f);
} }
При выполнении этой программы исключительной ситуации деления на нуль не возникает и выводятся следующие результаты:
С: \>java Ternary
а = 42
b = 2
с = 99
d = 0
a / b = 21
с / d= 0
Приоритеты операторов
В Java действует определенный порядок, или приоритет, операций. В элементарной алгебре нас учили тому, что у умножения и деления более высокий приоритет, чем у сложения и вычитания. В программировании также приходится следить и за приоритетами операций. В таблице указаны в порядке убывания приоритеты всех операций языка Java.
В первой строке таблицы приведены три необычных оператора, о которых мы пока не говорили. Круглые скобки () используются для явной установки приоритета. Как вы узнали из предыдущей главы, квадратные скобки используются для индексирования переменной-массива. Оператор. (точка) используется для выделения элементов из ссылки на объект - об этом мы поговорим в главе 7 . Все же остальные операторы уже обсуждались в этой главе.
Явные приоритеты
Поскольку высший приоритет имеют круглые скобки, вы всегда можете добавить в выражение несколько пар скобок, если у вас есть сомнения по поводу порядка вычислений или вам просто хочется сделать свои код более читабельным.
а >> b + 3
Какому из двух выражений, а >> (b + 3) или (а >> b) + 3, соответствует первая строка? Поскольку у оператора сложения более высокий приоритет, чем у оператора сдвига, правильный ответ - а>> (b + а). Так что если вам требуется выполнить операцию (а>>b)+ 3 без скобок не обойтись.
Итак, мы рассмотрели все виды операторов языка Java. Теперь вы можете сконструировать любое выражение с различными типами данных . В следующей главе познакомимся с конструкциями ветвления, организацией циклов и научимся управлять выполнением программы.
Большинство операций над примитивными типами выполняется не с помощью методов, а с помощью специальных символов, называемых знаком операции .
Побитовое И
int x = 112 ; int y = 94 ; int z; z = x & y; // z=80: 00000000 00000000 00000000 01010000Побитовое ИЛИ
int x = 112 ; // x: 00000000 00000000 00000000 01110000 int y = 94 ; // y: 00000000 00000000 00000000 01011110 int z; z = x | y; // z = 126: 00000000 00000000 00000000 01111110Побитовое исключающее ИЛИ
int x = 112 ; // x: 00000000 00000000 00000000 01110000 int y = 94 ; // y: 00000000 00000000 00000000 01011110 int z; z = x ^ y; // z = 46: 00000000 00000000 00000000 00101110Сдвиг влево с учетом знака
int x = 31 , z; // x: 00000000 00000000 00000000 00011111 z = x << 2 ; // z = 124: 00000000 00000000 00000000 01111100Сдвиг вправо с учетом знака
int x = - 17 , z; z = x >> 2 ; // z = -5: 11111111 11111111 11111111 11111011Сдвиг вправо без учета знака
int x = - 17 , z; // x: 11111111 11111111 11111111 11101111 z = x >>> 2 ; // z = 1073741819 // z: 00111111 11111111 11111111 11111011