Bitwise operators
Every whole number is stored in memory as a sequence of bits — zeros and ones. Bitwise operators work not with the number as a whole but with each of its bits separately. They are used for flags, masks, data encoding and fast calculations.
Binary form
Integer.toBinaryString() shows a number in binary. In code, a binary number is written with the 0b prefix:
void main() {
IO.println(Integer.toBinaryString(5));
IO.println(Integer.toBinaryString(12));
IO.println(0b1010);
}Output:
101
1100
10To turn a string of zeros and ones back into a number, use Integer.parseInt("1010", 2) — the second argument is the base.
AND, OR, exclusive OR
These operators compare the bits of two numbers pair by pair. & (AND) gives 1 only if both bits are 1; | (OR) — if at least one is 1; ^ (exclusive OR, XOR) — if the bits differ:
void main() {
int a = 0b1100;
int b = 0b1010;
IO.println(Integer.toBinaryString(a & b));
IO.println(Integer.toBinaryString(a | b));
IO.println(Integer.toBinaryString(a ^ b));
}Output:
1000
1110
110Do not confuse them with the logical && and || from the operators lesson: those work with boolean and skip the right side when the answer is already known.
Inversion ~
The ~ operator flips every bit: 0 becomes 1 and 1 becomes 0. Since the highest bit of an int holds the sign, the result is negative: ~n always equals -n - 1:
void main() {
IO.println(~5);
IO.println(Integer.toBinaryString(~5));
}Output:
-6
11111111111111111111111111111010Shifts
<< shifts the bits left, filling in zeros on the right — that is multiplying by 2 for each position. >> shifts right and keeps the sign — dividing by 2. >>> also shifts right but always fills in zeros on the left, even for negative numbers:
void main() {
IO.println(1 << 3);
IO.println(40 >> 2);
IO.println(-16 >> 2);
IO.println(-16 >>> 28);
}Output:
8
10
-4
15Flags and masks
Each bit of a number can hold a separate yes/no property — a flag. A number with the bits you need set is called a mask. | turns a flag on, & checks it, and & ~ turns it off:
void main() {
final int READ = 0b001;
final int WRITE = 0b010;
final int EXECUTE = 0b100;
int access = READ | WRITE;
IO.println("Read: " + ((access & READ) != 0));
IO.println("Execute: " + ((access & EXECUTE) != 0));
access = access & ~WRITE;
IO.println(Integer.toBinaryString(access));
}Output:
Read: true
Execute: false
1One int fits 32 such flags. In everyday code an EnumSet is usually used instead of masks — there is a lesson about it in the collections chapter.
Common mistake
The != comparison runs before &. Without parentheses Java first computes WRITE != 0 and then tries to apply & to a number and a boolean:
void main() {
int access = 0b011;
int WRITE = 0b010;
if (access & WRITE != 0) {
IO.println("Writing allowed");
}
}Output:
Main.java:4: error: bad operand types for binary operator '&'
if (access & WRITE != 0) {
^
first type: int
second type: boolean
1 error
error: compilation failedAlways put the bitwise part in parentheses: (access & WRITE) != 0.