«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(\u0026), 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. Побитовое И (\u0026)

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

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

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

@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. Побитовое дополнение (~)

Побитовое НЕ или оператор дополнения просто означает отрицание каждого бита входного значения. Он принимает только одно целое число и эквивалентен ! оператор.

Этот оператор изменяет каждую двоичную цифру целого числа, что означает, что все 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

«

result : -7

«Наконец, 0000 0111 — это 7 в десятичном виде. Поскольку бит знака был равен 1, как упоминалось выше, результирующий ответ:

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. Сдвиг влево со знаком [\u003c\u003c]

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

Еще один важный момент, который следует отметить, это то, что сдвиг числа на единицу эквивалентен умножению его на 2 или, вообще говоря, сдвиг числа влево на n позиций эквивалентно умножению на 2^n.

Возьмем значение 12 в качестве входного значения.

Теперь сдвинем его на 2 позиции влево (12 \u003c\u003c2) и посмотрим, что получится в итоге.

@Test
public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int leftShift = value << 2;
    assertEquals(48, leftShift);
}

Двоичный эквивалент числа 12 равен 00001100. После сдвига влево на 2 позиции результатом будет 00110000, что эквивалентно 48 в десятичном виде:

@Test
public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int leftShift = value << 2;
    assertEquals(-48, leftShift);
}

Это работает аналогично для отрицательного значения:

4.2. Signed Right Shift [\u003e\u003e]

    Оператор сдвига вправо сдвигает все биты вправо. Пустое пространство в левой части заполняется в зависимости от введенного числа:

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

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

Теперь сдвинем его на 2 места вправо (12 \u003e\u003e 2) и посмотрим, что получится в итоге.

@Test
public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int rightShift = value >> 2;
    assertEquals(3, rightShift);
}

Введенное число положительное, поэтому после сдвига вправо на 2 знака результат будет 0011, что равно 3 в десятичном виде:

@Test
public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int rightShift = value >> 2;
    assertEquals(-3, rightShift);
}

Также для отрицательного значения:

4.3 . Беззнаковый сдвиг вправо [\u003e\u003e\u003e]

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

@Test
public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int unsignedRightShift = value >>> 2;
    assertEquals(3, unsignedRightShift);
}

Сдвинем вправо то же самое значение 12:

@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. Заключение

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