Операторы Java ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-07-11

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

Побитовые (поразрядные) операции работают не с числом целиком, а с каждым его двоичным разрядом по отдельности. Java предоставляет четыре логические побитовые операции — & (И), | (ИЛИ), ^ (исключающее ИЛИ) и ~ (НЕ), — а также три битовых сдвига: <<, >> и >>>. На них построены маски битовых флагов, быстрая арифметика и даже простейшее шифрование — всё это разберём на примерах.

1. Побитовые операции: AND, OR, XOR, NOT

В следующей таблице перечислены побитовые операции языка Java:

Операция Название Описание
~ Поразрядное НЕ (NOT, дополнение) Унарная операция: инвертирует каждый бит операнда
& Поразрядное И (AND, поразрядная конъюнкция) Бит результата равен 1, если оба соответствующих бита равны 1
| Поразрядное ИЛИ (OR, поразрядная дизъюнкция) Бит результата равен 1, если хотя бы один из битов равен 1
^ Поразрядное исключающее ИЛИ (XOR) Бит результата равен 1, если биты операндов различаются

Побитовые операторы применяются к целочисленным типам long, int, short, byte и char — к каждому отдельному биту каждого операнда. Операторы &, | и ^ можно применять и к boolean, но тогда они работают как логические операторы без короткого замыкания.

Результаты выполнения побитовых логических операций для каждой пары битов:

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

1.1. Побитовое ИЛИ (OR, |)

Результирующий бит, полученный в результате выполнения оператора OR, равен 1, если соответствующий бит хотя бы в одном из операндов равен 1:

  00101010   42
| 00001111   15
  --------------
  00101111   47

1.2. Побитовое И (AND, &)

Значение бита, полученное в результате выполнения побитового оператора AND, &, равно 1, если соответствующие биты в обоих операндах также равны 1. Во всех остальных случаях значение результирующего бита равно 0:

  00101010   42
& 00001111   15
  --------------
  00001010   10

1.3. Побитовое исключающее ИЛИ (XOR, ^)

Результирующий бит, полученный в результате выполнения оператора XOR, ^, равен 1, если соответствующий бит только в одном из операндов равен 1. Во всех других случаях результирующий бит равен 0:

  00101010   42
^ 00001111   15
  --------------
  00100101   37

1.4. Побитовое НЕ (NOT, ~)

Унарный оператор NOT (НЕ), ~, называемый также побитовым дополнением, инвертирует все биты операнда:

~ 00101010   42
  --------------
  11010101

Обратите внимание: здесь для наглядности показаны только младшие восемь бит. В типе int инвертируются все 32 бита, включая знаковый, поэтому ~42 равно -43 — для любого числа справедлива формула ~x == -x - 1. Почему так получается, объясняет дополнительный код.

Рассмотрим теперь применение побитовых операций в программе. В следующем примере также показан метод Integer.toBinaryString(), который возвращает двоичную запись десятичного значения:

public class BitwiseExample1 {
    public static void main(String[] args) {
        int a = 3;
        int b = 6;
        int c = a | b;
        int d = a & b;
        int e = a ^ b;
        int f = ~b;
        System.out.println("a = " + Integer.toBinaryString(a));
        System.out.println("b = " + Integer.toBinaryString(b));
        System.out.println("a | b = " + Integer.toBinaryString(c));
        System.out.println("a & b = " + Integer.toBinaryString(d));
        System.out.println("a ^ b = " + Integer.toBinaryString(e));
        System.out.println("~ b = " + Integer.toBinaryString(f));
    }
}

Вывод программы (метод toBinaryString() не печатает ведущие нули):

a = 11
b = 110
a | b = 111
a & b = 10
a ^ b = 101
~ b = 11111111111111111111111111111001

2. Битовые сдвиги: >>, >>> и <<

Битовые сдвиги смещают все двоичные разряды значения на указанное количество позиций:

Операция Название Что происходит с битами
<< Сдвиг влево Биты смещаются влево, справа дописываются нули
>> Сдвиг вправо (арифметический) Биты смещаются вправо, слева дублируется знаковый бит — знак сохраняется
>>> Сдвиг вправо с заполнением нулями (беззнаковый) Биты смещаются вправо, слева дописываются нули независимо от знака

Общая форма:

значение << количество

Например:

34<<3, 56>>2, 78>>>1

Для того чтобы понять, как происходит сдвиг, лучше рассмотреть его на примере двоичных чисел:

0001<<1 = 0010
0100>>1 = 0010

При сдвиге отрицательных чисел проявляется разница между операторами >> и >>>. Операция >> распространяет знаковый (левый) бит направо, а >>> заполняет освободившиеся разряды нулями. У положительных чисел результат обеих операций одинаков (отрицательные числа в примере записаны в дополнительном коде):

int i = 192;
i       00000000 00000000 00000000 11000000 (192)
i<<1    00000000 00000000 00000001 10000000 (384)
i>>1    00000000 00000000 00000000 01100000 (96)
i>>>1   00000000 00000000 00000000 01100000 (96)

int i = -192; (двоичная запись в доп. коде)
i       11111111 11111111 11111111 01000000 (-192)
i<<1    11111111 11111111 11111110 10000000 (-384)
i>>1    11111111 11111111 11111111 10100000 (-96)
i>>>1   01111111 11111111 11111111 10100000 (2147483552)

Обратите внимание

Для отрицательного числа >>> обнуляет знаковый бит, поэтому результат — всегда большое положительное число: -192 >>> 1 даёт 2147483552, а вовсе не «минус 96 без знака». Если нужно поделить отрицательное число на степень двойки с сохранением знака — используйте >>.

Типы byte и short продвигаются к типу int при вычислении выражения. Следующий пример показывает, к чему это приводит:

public class BitwiseExample2 {
    public static void main(String[] args) {
        byte a = 64; //0100 0000
        byte b;
        int i = a << 2; // 1 0000 0000
        b = (byte) (a << 2); //0000 0000
        System.out.println("a = " + a);
        System.out.println("i = " + i);
        System.out.println("b = " + b);
    }
}

Вывод программы:

a = 64
i = 256
b = 0

Важно

Результат сдвига a << 2 имеет тип int, даже если a объявлена как byte или short. В int значение 256 помещается, но при явном приведении обратно к byte старшие биты отбрасываются — остаётся 0. Записать результат в byte без приведения не получится: byte b = a << 2; не скомпилируется.

3. Побитовые операции с присваиванием

Каждой побитовой операции соответствует составная операция с присваиванием:

Операция Описание Эквивалент
&= Поразрядное И с присваиванием x &= y  →  x = x & y
|= Поразрядное ИЛИ с присваиванием x |= y  →  x = x | y
^= Поразрядное исключающее ИЛИ с присваиванием x ^= y  →  x = x ^ y
>>= Сдвиг вправо с присваиванием x >>= n  →  x = x >> n
>>>= Сдвиг вправо с заполнением нулями и присваиванием x >>>= n  →  x = x >>> n
<<= Сдвиг влево с присваиванием x <<= n  →  x = x << n

Есть один нюанс: составное присваивание выполняет неявное приведение к типу переменной. Поэтому byte b = 64; b <<= 2; компилируется (и по правилам из предыдущего раздела даёт 0), хотя b = b << 2; — ошибка компиляции.

4. Практическое применение побитовых операций

Побитовые операции имеют довольно широкое практическое применение. Рассмотрим некоторые случаи.

4.1. Чётность числа

Выражение x & 1 проверяет чётность числа: у чётного числа младший бит равен 0, у нечётного — 1. Поэтому результат равен 0, если число чётное, и 1, если нечётное. В условии скобки обязательны — (x & 1) == 0: из-за более низкого приоритета & запись без скобок была бы разобрана как x & (1 == 0) и не скомпилировалась.

4.2. Умножение и деление на два сдвигами

  • x<<1 – умножение на 2;
  • x>>1 – деление на два с отбрасыванием любого остатка.

Каждая дополнительная позиция сдвига — ещё одно умножение или деление на 2: x<<3 умножает на 8. Для отрицательных чисел x>>1 округляет вниз, а не к нулю, поэтому результат может отличаться от x/2: -5 >> 1 равно -3, тогда как -5/2 равно -2.

4.3. Шифрование числа

Операция XOR при применении два раза к одному и тому же битовому массиву восстанавливает его исходное значение. Это можно использовать при шифровании данных для передачи по сети:

C = A ^ B
A = C ^ B

Представьте, что необходимо отправить в сообщении число 560 — пин-код от банковской карты. Если злоумышленник перехватит сообщение, то узнает пин-код и сможет воспользоваться им. Чтобы этого не произошло, придумаем какое-то число — маску — и сообщим его получателю заранее. Перед отправкой зашифруем пин-код побитовой операцией XOR: message ^ mask — и отправим результат. Даже если злоумышленник перехватит сообщение, он не будет знать, как его расшифровать. Адресат же восстанавливает пин-код с помощью имеющейся маски: codedMessage ^ mask.

Следующий код иллюстрирует этот пример:

public class BitwiseExample3 {
    public static void main(String[] args) {
        int message = 560;
        int mask = 67;
        int codedMessage = message ^ mask;
        int receivedMessage = codedMessage ^ mask;
        System.out.println("message = " + message);
        System.out.println("message = " + Integer.toBinaryString(message));
        System.out.println("codedMessage = " + codedMessage);
        System.out.println("codedMessage = " + Integer.toBinaryString(codedMessage));
        System.out.println("receivedMessage = " + receivedMessage);
        System.out.println("receivedMessage = " + Integer.toBinaryString(receivedMessage));
    }
}

Вывод программы — после повторного XOR с той же маской получатель видит исходные 560:

message = 560
message = 1000110000
codedMessage = 627
codedMessage = 1001110011
receivedMessage = 560
receivedMessage = 1000110000

4.4. Наложение маски

Маска позволяет получать значения только определённых битов в последовательности. Например, у нас есть маска 00100100. Она позволяет получать из последовательности только те биты, которые в ней установлены, — в данном случае это 3-й и 6-й разряд. Для этого достаточно выполнить побитовое AND с нашей маской и выбранным числом:

  001010101
& 000100100
  --------------
  000000100

Часто задаваемые вопросы

Где побитовые операции применяются в реальном коде?

Чаще всего — для битовых флагов и масок: одно int-поле хранит до 32 настроек, флаг проверяется через &, устанавливается через |, сбрасывается через & ~. Кроме того, побитовые операции используются в сетевых протоколах и бинарных форматах файлов, при работе с цветом (выделение RGB-каналов), в хеш-функциях и криптографии. Даже стандартная библиотека Java применяет их: например, HashMap вычисляет индекс корзины через (n - 1) & hash.

Чем отличается >> от >>> в Java?

Оператор >> — арифметический сдвиг: слева дублируется знаковый бит, поэтому знак числа сохраняется. Оператор >>> — беззнаковый сдвиг: слева всегда дописываются нули. Для положительных чисел результат одинаков, а для отрицательных различается кардинально: -192 >> 1 даёт -96, а -192 >>> 1 — 2147483552, потому что знаковый бит обнуляется и число становится большим положительным.

Почему ~x равно -x - 1?

Из-за дополнительного кода, в котором Java хранит целые числа: противоположное число получается инверсией всех битов плюс единица, то есть -x == ~x + 1. Перенеся единицу, получаем ~x == -x - 1. Проверить легко: ~42 равно -43, а ~0 равно -1 (все 32 бита становятся единицами).

Как проверить чётность числа побитовой операцией?

Выражением (x & 1) == 0 — оно истинно для чётных чисел. Маска 1 оставляет только младший бит, который у чётных чисел равен 0, а у нечётных 1. Способ корректно работает и для отрицательных чисел благодаря дополнительному коду. Скобки вокруг x & 1 обязательны: приоритет & ниже, чем у ==, и без них выражение не скомпилируется.

Презентацию с видео можно скачать на Patreon.

Video Explanation

Prefer video format? Watch this lesson with examples and explanations.

Comments

TTereshchenko May 19, 2021
3. Побитовые операции с присваиванием. >>­Сдвиг вправо с присваиванием - отсутствует "="
May 19, 2021
Спасибо! Исправлено.
unknown Oct 22, 2022
Побитовые операторы выполняют побитовые действия над двумя выражениями с любым типом данных, относящимся к категории типа данных integer. При чём здесь символьный тип данных "char"?
milkina Oct 24, 2022
Тип char совместим с целочисленными типами. К нему можно применять арифметические операции, а также побитовые операции.

Please log in or register to have a possibility to add comment.