Java Bitwise Operators
Table of Contents + −
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.
5is101(one 4, zero 2s, one 1).6is110(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
101110📋 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
1only when both input bits are1. - Any
0in the pair gives0.
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; // 1100int b = 10; // 1010int 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:
1100and1010, left to right: 1&1=1, 1&0=0, 0&1=0, 0&0=0.- Result is
1000, which is8.
Output
1100101010008So & 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
1when at least one of the two bits is1. - Only gives
0when both bits are0.
int a = 12; // 1100int b = 10; // 1010int 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
111014So | 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
1only 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; // 1100int b = 10; // 1010int 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
1106Remember 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
1becomes0, each0becomes1. - 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:
~nequals-(n + 1). - So
~5is-(5 + 1)=-6, and~0is-1.
Output
-6Tip
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; // 11System.out.println(Integer.toBinaryString(a << 1));System.out.println(a << 1);System.out.println(a << 3);Start with 3, which is 11:
a << 1turns11into110=6(doubled).a << 3turns11into11000=24(3 times 8).- Pattern:
n << kequalsnmultiplied by 2 to the powerk.
Output
110624So << 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
0on the left, negatives fill with1, so negatives stay negative.
int a = 24; // 11000System.out.println(a >> 1);System.out.println(a >> 3);
int b = -24;System.out.println(b >> 1);24 >> 1gives12(24 / 2).24 >> 3gives3(24 / 8).-24 >> 1stays negative and gives-12.
Output
123-12So >> 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 >> 1stays negative and becomes-4.-8 >>> 1fills the left with0, so the old sign bit becomes data and the number turns into a very large positive value.
Output
-42147483644Reach 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; // 001int NOTIFICATIONS = 2; // 010int SOUND = 4; // 100
int settings = 0;settings = settings | DARK_MODE; // turn dark mode onsettings = settings | SOUND; // turn sound on
System.out.println(Integer.toBinaryString(settings));
// Check whether each setting is onSystem.out.println((settings & DARK_MODE) != 0);System.out.println((settings & NOTIFICATIONS) != 0);System.out.println((settings & SOUND) != 0);- Start with
0(all off), then|withDARK_MODEandSOUNDswitches both bits on, sosettingsbecomes101. - To check, AND
settingswith 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
101truefalsetrueThis 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
1means odd;0means even. - So
n & 1keeps only the last bit and gives the answer.
int x = 7;int y = 10;System.out.println(x & 1); // 1 means oddSystem.out.println(y & 1); // 0 means even7 & 1is1(last bit set), so 7 is odd.10 & 1is0(last bit clear), so 10 is even.
Output
10Another 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
95Tip
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 numbersint onlyLastBit = value & 1;
// ✅ Good: use && for combining conditionsif (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 shiftint raw = -8 >>> 1; // gives a large positive numberForgetting 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 bracketsif ((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) != 0so 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_MODEis far clearer than a bare1scattered 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~nequals-(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
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
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
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
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.