1. Обзор
Операторы используются в языке Java для работы с данными и переменными.
В этом уроке мы рассмотрим побитовые операторы и то, как они работают в Java.
2. Побитовые операторы
Побитовые операторы работают с двоичными цифрами или битами входных значений. Мы можем применить их к целочисленным типам – long, int, short, char, и байт.
Прежде чем исследовать различные побитовые операторы, давайте сначала поймем, как они работают.
Побитовые операторы работают с двоичным эквивалентом десятичных чисел и выполняют операции над ними по битам в соответствии с данным оператором:
- Во-первых, операнды преобразуются в их двоичное представление
- Затем оператор применяется к каждому двоичному числу и вычисляется результат
- Наконец, результат преобразуется обратно в десятичное представление
Давайте разберемся на примере; возьмем два целых числа:
int value1 = 6; int value2 = 5;
Далее, давайте применим побитовый оператор ИЛИ к этим числам:
int result = 6 | 5;
Для выполнения этой операции сначала будет вычислено двоичное представление этих чисел:
Binary number of value1 = 0110 Binary number of value2 = 0101
Затем операция будет применена к каждому биту. Результат возвращает новое двоичное число:
0110 0101 ----- 0111
Наконец, результат 0111 будет преобразован обратно в десятичную дробь, которая равна 7 :
result : 7
Побитовые операторы далее классифицируются как побитовые логические и побитовые операторы сдвига. Давайте теперь рассмотрим каждый тип.
3. Побитовые Логические Операторы
Побитовыми логическими операторами являются AND(&), OR(|), XOR (^) и NOT(~).
3.1. Побитовое ИЛИ (|)
Оператор OR сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если любое из них равно 1.
Это похоже на логический оператор , используемый с boolean. При сравнении двух логических значений результатом является true , если любое из них true.
Мы видели пример этого оператора в предыдущем разделе:
@Test public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 | value2; assertEquals(7, result); }
Давайте посмотрим двоичное представление этой операции:
0110 0101 ----- 0111
Здесь мы видим, что использование OR, 0 и 0 приведет к 0, в то время как любая комбинация, по крайней мере, с 1, приведет к 1.
3.2. Побитовое И (&)
Оператор AND сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если оба равны 1, в противном случае он возвращает 0.
Это похоже на оператор && со значениями boolean . Когда значения двух логических равны true , результатом операции && является true.
Давайте воспользуемся тем же примером, что и выше, за исключением использования оператора & вместо оператора |:
@Test public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 & value2; assertEquals(4, result); }
Давайте также рассмотрим двоичное представление этой операции:
0110 0101 ----- 0100
0100 есть 4 таким образом, в десятичной системе счисления результат:
result : 4
3.3. Побитовое XOR (^)
Оператор XOR сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если оба сравниваемых бита различны. Это означает, что если биты обоих целых чисел равны 1 или 0, результат будет равен 0; в противном случае результат будет равен 1:
@Test public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() { int value1 = 6; int value2 = 5; int result = value1 ^ value2; assertEquals(3, result); }
И двоичное представление:
0110 0101 ----- 0011
0011 равно 3 в десятичной дроби, поэтому результат:
result : 3
3.4. Побитовое ДОПОЛНЕНИЕ (~)
Побитовый оператор Not или Complement просто означает отрицание каждого бита входного значения. Для этого требуется только одно целое число, и оно эквивалентно ! оператор.
Этот оператор изменяет каждую двоичную цифру целого числа, что означает, что все 0 становятся 1, а все 1 становятся 0. ! оператор работает аналогично для boolean значений: он меняет boolean значения с true на false и наоборот.
Теперь давайте разберемся на примере, как найти дополнение десятичного числа.
Давайте сделаем дополнение:
@Test public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() { int value1 = 6; int result = ~value1; assertEquals(-7, result); }
Значение в двоичном коде равно:
value1 = 0000 0110
Применяя оператор дополнения, результат будет:
0000 0110 -> 1111 1001
Это дополнение единицы к десятичному числу 6. А поскольку первый (крайний левый) бит равен 1 в двоичном коде, это означает, что знак отрицателен для числа, которое хранится.
Теперь, поскольку числа хранятся в виде дополнения 2, сначала нам нужно найти его дополнение 2, а затем преобразовать полученное двоичное число в десятичное число:
1111 1001 -> 0000 0110 + 1 -> 0000 0111
Наконец, 0000 0111-это 7 в десятичной системе счисления. Поскольку знаковый бит был равен 1, как упоминалось выше, поэтому результирующий ответ:
result : -7
3.5. Таблица побитовых операторов
Давайте обобщим результаты операторов, которые мы видели до сих пор, в сравнительной таблице:
A B A|B A&B A^B ~A 0 0 0 0 0 1 1 0 1 0 1 0 0 1 1 0 1 1 1 1 1 1 0 0
4. Операторы Побитового Сдвига
Двоичные операторы сдвига сдвигают все биты входного значения влево или вправо в зависимости от оператора сдвига.
Давайте посмотрим синтаксис этих операторов:
value
Левая часть выражения-это целое число, которое сдвигается, а правая часть выражения обозначает количество раз, когда оно должно быть сдвинуто.
Операторы побитового сдвига далее классифицируются как операторы побитового сдвига влево и побитового сдвига вправо.
4.1. Подписанный сдвиг Влево [<<]
Оператор сдвига влево сдвигает биты влево на количество раз, указанное правой частью операнда. После сдвига влево пустое пространство справа заполняется 0.
Еще один важный момент, который следует отметить, заключается в том, что сдвиг числа на единицу эквивалентен умножению его на 2, или, в общем случае, сдвиг числа влево на n позиций эквивалентен умножению на 2^ n .
Давайте возьмем значение 12 в качестве входного значения.
Теперь мы переместим его на 2 места влево (12 <<2) и посмотрим, каков будет конечный результат.
Двоичный эквивалент 12 равен 00001100. После сдвига влево на 2 места результат равен 00110000, что эквивалентно 48 в десятичной системе счисления:
@Test public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() { int value = 12; int leftShift = value << 2; assertEquals(48, leftShift); }
Это работает аналогично для отрицательного значения:
@Test public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() { int value = -12; int leftShift = value << 2; assertEquals(-48, leftShift); }
4.2. Подписанный Сдвиг Вправо [>>]
Оператор сдвига вправо сдвигает все биты вправо. Пустое пространство в левой части заполняется в зависимости от входного номера:
- Если входное число отрицательное, где крайний левый бит равен 1, то пустые места будут заполнены 1
- Если входное число положительное, где крайний левый бит равен 0, то пустые места будут заполнены 0
Давайте продолжим пример, используя 12 в качестве входных данных.
Теперь мы переместим его на 2 места вправо(12 >>2) и посмотрим, каков будет конечный результат.
Входное число положительное, поэтому после сдвига вправо на 2 места результат равен 0011, что равно 3 в десятичной системе счисления:
@Test public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() { int value = 12; int rightShift = value >> 2; assertEquals(3, rightShift); }
Кроме того, для отрицательного значения:
@Test public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() { int value = -12; int rightShift = value >> 2; assertEquals(-3, rightShift); }
4.3. Сдвиг Вправо Без знака [>>>]
Этот оператор очень похож на оператор сдвига вправо со знаком. Единственное различие заключается в том, что пустые места слева заполняются 0 независимо от того, является ли число положительным или отрицательным. Следовательно, результатом всегда будет положительное целое число.
Давайте сдвинем вправо то же значение 12:
@Test public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() { int value = 12; int unsignedRightShift = value >>> 2; assertEquals(3, unsignedRightShift); }
А теперь отрицательное значение:
@Test public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() { int value = -12; int unsignedRightShift = value >>> 2; assertEquals(1073741821, unsignedRightShift); }
5. Разница между побитовыми и логическими операторами
Существует несколько различий между побитовыми операторами, которые мы обсуждали здесь, и более известными логическими операторами.
Во-первых, логические операторы работают с логическими выражениями и возвращают логические значения (либо true или false), в то время как побитовые операторы работают с двоичными цифрами целочисленных значений ( long, int, short, char, и byte ) и возвращают целое число.
Кроме того, логические операторы всегда вычисляют первое логическое выражение и, в зависимости от его результата и используемого оператора, могут вычислять или не вычислять второе. С другой стороны, побитовые операторы всегда вычисляют оба операнда .
Наконец, логические операторы используются при принятии решений на основе нескольких условий, в то время как побитовые операторы работают с битами и выполняют побитовые операции.
6. Примеры использования
Некоторые потенциальные варианты использования побитовых операторов:
- Стеки связи, в которых отдельные биты в заголовке, прикрепленном к данным, обозначают важную информацию
- Во встроенных системах для установки/очистки/переключения только одного бита определенного регистра без изменения остальных битов
- Шифрование данных для обеспечения безопасности с помощью оператора XOR
- При сжатии данных путем преобразования данных из одного представления в другое, чтобы уменьшить объем используемого пространства
7. Заключение
В этом уроке мы узнали о типах побитовых операторов и о том, чем они отличаются от логических операторов. Мы также видели некоторые потенциальные варианты их использования.
Все примеры кода в этой статье доступны на GitHub .