Перейти к основному содержимому

Побитовые операторы Java

· 8 мин. чтения

1. Обзор

Операторы используются в языке Java для работы с данными и переменными.

В этом руководстве мы рассмотрим побитовые операторы и то, как они работают в Java.

2. Побитовые операторы

Побитовые операторы работают с двоичными цифрами или битами входных значений. Мы можем применить их к целочисленным типам — long, int, short, char и byte.

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

Побитовые операторы работают с двоичным эквивалентом десятичных чисел и выполняют над ними операции побитно в соответствии с данным оператором:

  • Сначала операнды преобразуются в их двоичное представление.
  • Далее оператор применяется к каждому двоичному числу и вычисляется результат
  • Наконец, результат преобразуется обратно в десятичное представление.

Давайте разберемся на примере; возьмем два целых числа:

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. Побитовое ИЛИ (|)

Оператор ИЛИ сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если одно из них равно 1.

Это похоже на || логический оператор, используемый с логическими значениями. При сравнении двух логических значений результат истинен , если истинно любое из них . Точно так же выход равен 1, когда любой из них равен 1.

Мы видели пример этого оператора в предыдущем разделе:

@Test
public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 | value2;
assertEquals(7, result);
}

Давайте посмотрим на бинарное представление этой операции:

0110
0101
-----
0111

Здесь мы видим, что использование ИЛИ, 0 и 0 приведет к 0, а любая комбинация хотя бы с 1 приведет к 1.

3.2. Побитовое И (&)

Оператор И сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если оба равны 1, иначе возвращает 0.

Это похоже на оператор && с логическими значениями. Когда значения двух логических значений истинны , результат операции && истинен .

Давайте используем тот же пример, что и выше, за исключением того, что теперь мы используем оператор & вместо | оператор:

@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 сравнивает каждую двоичную цифру двух целых чисел и возвращает 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. Оператор ! оператор работает аналогично для логических значений: он меняет логические значения с истинного на ложное и наоборот.

Теперь разберемся на примере, как найти дополнение десятичного числа.

Сделаем дополнение value1 = 6:

@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 <operator> <number_of_times>

Левая часть выражения — это целое число, которое сдвигается, а правая часть выражения обозначает, сколько раз его нужно сдвинуть.

Операторы побитового сдвига далее классифицируются как операторы побитового сдвига влево и побитового сдвига вправо.

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. Разница между побитовыми и логическими операторами

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

Во- первых, логические операторы работают с логическими выражениями и возвращают логические значения ( истина или ложь), тогда как побитовые операторы работают с двоичными цифрами целых значений ( long, int, short, char и byte ) и возвращают целое число.

Кроме того, логические операторы всегда оценивают первое логическое выражение и, в зависимости от его результата и используемого оператора, могут оценивать или не оценивать второе. С другой стороны, побитовые операторы всегда оценивают оба операнда .

Наконец, логические операторы используются для принятия решений на основе нескольких условий, в то время как побитовые операторы работают с битами и выполняют побитовые операции.

6. Варианты использования

Некоторые потенциальные варианты использования побитовых операторов:

  • Коммуникационные стеки, в которых отдельные биты в заголовке, прикрепленном к данным, обозначают важную информацию.
  • Во встроенных системах для установки/очистки/переключения только одного бита определенного регистра без изменения остальных битов.
  • Чтобы зашифровать данные для обеспечения безопасности с помощью оператора XOR
  • При сжатии данных путем преобразования данных из одного представления в другое, чтобы уменьшить объем используемого пространства.

7. Заключение

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

Все примеры кода в этой статье доступны на GitHub .