Рубрики
Без рубрики

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

Узнайте о побитовых операторах в Java и о том, как каждый из них работает

Автор оригинала: baeldung.

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 .