Java Bitwise Operators

In the last lesson you learned about Java unary operators. Now we go deeper, down to the actual bits inside a number. Bitwise operators let you flip, shift, and combine the 1s and 0s a number is built from.

🤔 Why look at the bits?

A computer stores every int as a row of 32 bits, each a single 1 or 0. Usually Java hides them. Sometimes the bits themselves are the data.

  • Store ten yes-or-no settings in one number instead of ten booleans.
  • Multiply or divide by powers of two faster than normal math.
  • Check if a number is even without dividing.

Binary writes numbers using only 1 and 0. Each position is worth double the one on its right: 1, 2, 4, 8, 16, and so on.

  • 5 is 101 (one 4, zero 2s, one 1).
  • 6 is 110 (one 4, one 2, zero 1s).

Integer.toBinaryString(n) turns a number into its binary text so you can read it. We use it a lot below.

System.out.println(Integer.toBinaryString(5));
System.out.println(Integer.toBinaryString(6));

Java drops the leading zeros, so you only see the meaningful part.

Output

101
110

📋 The bitwise operators at a glance

Java has seven bitwise operators. Here is the quick map; each gets its own example below.

Operator Name What it does
& AND 1 only when both bits are 1
| OR 1 when at least one bit is 1
^ XOR 1 when the two bits are different
~ complement Flips every bit
<< left shift Moves bits left, fills with 0
>> signed right shift Moves bits right, keeps the sign
>>> unsigned right shift Moves bits right, fills with 0

The first four work bit by bit. The last three slide the whole row of bits left or right.

✅ The AND operator (&)

The bitwise AND operator & compares two numbers bit by bit:

  • Result bit is 1 only when both input bits are 1.
  • Any 0 in the pair gives 0.

Here is the truth for a single pair of bits.

A B A & B
0 0 0
0 1 0
1 0 0
1 1 1

Let’s AND 12 and 10 together.

int a = 12; // 1100
int b = 10; // 1010
int result = a & b;
System.out.println(Integer.toBinaryString(a));
System.out.println(Integer.toBinaryString(b));
System.out.println(Integer.toBinaryString(result));
System.out.println(result);

Line up the bits and check each column:

  • 1100 and 1010, left to right: 1&1=1, 1&0=0, 0&1=0, 0&0=0.
  • Result is 1000, which is 8.

Output

1100
1010
1000
8

So & keeps only the bits set in both numbers, which is perfect for checking flags later.

🔵 The OR operator (|)

The bitwise OR operator |:

  • Result bit is 1 when at least one of the two bits is 1.
  • Only gives 0 when both bits are 0.
int a = 12; // 1100
int b = 10; // 1010
int result = a | b;
System.out.println(Integer.toBinaryString(result));
System.out.println(result);

Check the columns: 1100 | 1010 gives 1, 1, 1, 0. Result is 1110, which is 14.

Output

1110
14

So | collects every bit set in either number, which is the natural way to turn a setting on.

🔀 The XOR operator (^)

The XOR operator ^ means “exclusive or”:

  • Result bit is 1 only when the two bits are different.
  • Same bits (both 1 or both 0) give 0.
A B A ^ B
0 0 0
0 1 1
1 0 1
1 1 0
int a = 12; // 1100
int b = 10; // 1010
int result = a ^ b;
System.out.println(Integer.toBinaryString(result));
System.out.println(result);

Column by column, 1100 ^ 1010 gives 0, 1, 1, 0. Result is 0110, which is 6.

Output

110
6

Remember this XOR habit: applying the same value twice cancels it out, so a ^ b ^ b equals a. That powers the XOR swap trick at the end.

🔄 The complement operator (~)

The complement operator ~ works on one number:

  • Flips every bit: each 1 becomes 0, each 0 becomes 1.
  • It touches all 32 bits, so the result can look surprising.
int a = 5;
int result = ~a;
System.out.println(result);

You get -6, not a small number, because Java stores negatives using two’s complement.

  • The rule that always holds: ~n equals -(n + 1).
  • So ~5 is -(5 + 1) = -6, and ~0 is -1.

Output

-6

Tip

An easy way to remember the result of ~ is the formula ~n == -(n + 1). So ~0 is -1, ~1 is -2, and ~-1 is 0.

⬅️ The left shift operator

The left shift operator << slides every bit to the left:

  • Gaps on the right fill with 0.
  • Each shift left doubles the number, since every bit moves to a position worth double.
int a = 3; // 11
System.out.println(Integer.toBinaryString(a << 1));
System.out.println(a << 1);
System.out.println(a << 3);

Start with 3, which is 11:

  • a << 1 turns 11 into 110 = 6 (doubled).
  • a << 3 turns 11 into 11000 = 24 (3 times 8).
  • Pattern: n << k equals n multiplied by 2 to the power k.

Output

110
6
24

So << is a very fast way to multiply by powers of two.

➡️ The signed right shift operator

The signed right shift operator >> slides bits to the right:

  • Each shift right roughly halves the number, dropping any remainder.
  • “Signed” means it keeps the sign: positives fill with 0 on the left, negatives fill with 1, so negatives stay negative.
int a = 24; // 11000
System.out.println(a >> 1);
System.out.println(a >> 3);
int b = -24;
System.out.println(b >> 1);
  • 24 >> 1 gives 12 (24 / 2).
  • 24 >> 3 gives 3 (24 / 8).
  • -24 >> 1 stays negative and gives -12.

Output

12
3
-12

So >> is a fast divide by a power of two that respects the sign.

⏩ The unsigned right shift operator

The unsigned right shift operator >>> also slides bits right:

  • It always fills the left with 0, whatever the sign.
  • For positives it acts exactly like >>.
  • The difference shows up only with negatives, where the sign bit is no longer protected.
int a = -8;
System.out.println(a >> 1);
System.out.println(a >>> 1);
  • -8 >> 1 stays negative and becomes -4.
  • -8 >>> 1 fills the left with 0, so the old sign bit becomes data and the number turns into a very large positive value.

Output

-4
2147483644

Reach for >>> when you treat a number as a raw row of bits, such as packing data or reading low-level formats.

🚩 Practical use: flags and bitmasks

Store several on/off settings inside one int instead of many booleans, each setting in its own bit. This is a bitmask.

  • Give each setting a value that is a single bit.
  • Turn a setting on with |.
  • Check whether a setting is on with &.
int DARK_MODE = 1; // 001
int NOTIFICATIONS = 2; // 010
int SOUND = 4; // 100
int settings = 0;
settings = settings | DARK_MODE; // turn dark mode on
settings = settings | SOUND; // turn sound on
System.out.println(Integer.toBinaryString(settings));
// Check whether each setting is on
System.out.println((settings & DARK_MODE) != 0);
System.out.println((settings & NOTIFICATIONS) != 0);
System.out.println((settings & SOUND) != 0);
  • Start with 0 (all off), then | with DARK_MODE and SOUND switches both bits on, so settings becomes 101.
  • To check, AND settings with the setting’s bit; a non-zero result means it was on.
  • Dark mode and sound come back true; notifications, never turned on, comes back false.

Output

101
true
false
true

This trick is everywhere in real software, from file permissions to graphics options, because it packs many switches into one tidy number.

⚖️ Practical use: even or odd, and the XOR swap

The last bit tells you instantly whether a number is even or odd, with no division:

  • Last bit 1 means odd; 0 means even.
  • So n & 1 keeps only the last bit and gives the answer.
int x = 7;
int y = 10;
System.out.println(x & 1); // 1 means odd
System.out.println(y & 1); // 0 means even
  • 7 & 1 is 1 (last bit set), so 7 is odd.
  • 10 & 1 is 0 (last bit clear), so 10 is even.

Output

1
0

Another classic trick swaps two numbers with XOR and no temporary variable. It leans on that XOR habit where applying a value twice cancels it out.

int p = 5;
int q = 9;
p = p ^ q;
q = p ^ q;
p = p ^ q;
System.out.println(p);
System.out.println(q);

After the three XOR steps the values trade places: p holds the old q, and q holds the old p. Each step rebuilds one original value from the combined result.

Output

9
5

Tip

The XOR swap is a fun trick to know, but in everyday code a simple temporary variable is clearer and just as fast. Reach for it mainly to understand how XOR behaves.

⚠️ Common Mistakes

A few bitwise traps catch nearly everyone early on.

Confusing & with &&. Single & works on bits and gives a number; double && combines booleans and gives true or false. They are not interchangeable.

// ❌ Avoid: & on booleans works but does not short-circuit, and reads as a typo
// if (user != null & user.isActive()) ...
// ✅ Good: use & for bit math on numbers
int onlyLastBit = value & 1;
// ✅ Good: use && for combining conditions
if (user != null && user.isActive()) {
System.out.println("Active");
}

Using >> when you needed >>>. The signed shift keeps the sign, so a negative stays negative. That surprises you when you mean to treat the value as raw bits.

// ❌ Avoid: >> keeps the sign, so a negative stays negative
// int wrong = -8 >> 1; // gives -4
// ✅ Good: >>> fills with zeros when you want a raw bit shift
int raw = -8 >>> 1; // gives a large positive number

Forgetting that bitwise operators have low precedence. In Java, & and | bind looser than comparisons like ==, so a flag check without brackets does the wrong thing.

// ❌ Avoid: this is read as settings & (DARK_MODE != 0), which is wrong
// if (settings & DARK_MODE != 0) ...
// ✅ Good: wrap the bitwise part in brackets
if ((settings & DARK_MODE) != 0) {
System.out.println("Dark mode is on");
}

✅ Best Practices

A few habits keep bit work clear and correct:

  • Print bits while learning. Use Integer.toBinaryString(n) to see what is happening, so the result is never a mystery.
  • Always bracket flag checks. Write (settings & FLAG) != 0 so precedence never bites you.
  • Use & for bits and && for conditions. Keep the single and double forms in their own lanes.
  • Name your bit constants. A name like DARK_MODE is far clearer than a bare 1 scattered through the code.
  • Pick >> for signed numbers and >>> for raw bits. Choose the shift that matches how you are treating the value.

🧩 What You’ve Learned

Nice work. The key points:

  • ✅ Numbers are stored as bits, and Integer.toBinaryString(n) lets you read them.
  • & (AND) keeps bits set in both numbers; | (OR) keeps bits set in either; ^ (XOR) keeps bits that differ.
  • ~ (complement) flips every bit, and ~n equals -(n + 1).
  • << doubles by shifting left; >> halves while keeping the sign; >>> shifts right filling with zeros.
  • ✅ Bitwise tools power flags and bitmasks, even/odd checks with & 1, and the XOR swap.
  • ✅ Bracket flag checks because bitwise operators have low precedence.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is the result of 12 & 10 in decimal?

    Why: AND keeps only bits set in both. 1100 & 1010 is 1000, which is 8.

  2. 2

    What does n << 3 do to a number?

    Why: Each left shift doubles the value, so shifting left by 3 multiplies by 2 to the power 3, which is 8.

  3. 3

    How can you tell if an int n is odd using bits?

    Why: The last bit is 1 for odd numbers, so n & 1 keeps that bit and equals 1 when n is odd.

  4. 4

    What is the difference between >> and >>> ?

    Why: Signed right shift >> preserves the sign bit, while unsigned right shift >>> always fills the left side with zeros.

🚀 What’s Next?

You can now work right down at the bit level. Next we look at a compact operator that picks between two values in a single line, a neat shortcut for short if-else choices.

Java Ternary Operator

Share & Connect