← Back to Home

Bitwise Operators in Java

Bitwise operators in Java represent one of the most fundamental yet often misunderstood areas of the language. While most developers spend their time working with high-level abstractions such as objects, collections, and frameworks, bitwise operators operate at a much lower level—directly manipulating the binary representation of data. This makes them extremely powerful, especially in scenarios involving performance optimization, memory-efficient operations, and system-level programming.

Bitwise Operators in Java

Although bitwise operators are not used frequently in everyday business applications like web development or enterprise systems, they are highly valued in interviews and core Java understanding. They test a developer’s ability to think at a deeper level, beyond syntax and APIs, and understand how data is actually stored and processed inside the system.

Understanding Bitwise Operations

At its core, every value in a computer is stored in binary form—a sequence of bits consisting of 0s and 1s. Bitwise operators allow you to perform operations directly on these bits rather than on the decimal representation of numbers.

In Java, bitwise operators work with integral data types such as byte, short, int, long, and char. They are not typically used with boolean, except in specific cases where & and | can act as logical operators without short-circuiting.

The key idea behind bitwise operations is simple: instead of working with whole numbers, you manipulate each bit individually. This allows for highly efficient computations and fine-grained control over data.

Types of Bitwise Operators

Java provides a complete set of bitwise operators that cover logical operations and bit shifting. These include AND, OR, XOR, complement, and shift operators. Each operator has a specific role and behavior, and understanding them requires thinking in binary rather than decimal.

Bitwise AND (&)

The bitwise AND operator compares corresponding bits of two numbers. A result bit is set to 1 only if both corresponding bits are 1; otherwise, it is set to 0.

Consider two numbers: 5 and 3. In binary, 5 is represented as 0101 and 3 as 0011. When you apply the AND operation, you compare each bit position. Only the last bit position has both bits set to 1, so the result becomes 0001, which is 1 in decimal.

This operator is commonly used for masking, where specific bits are isolated while others are ignored. For example, you can use AND to check whether a particular bit is set in a number.

Bitwise OR (|)

The bitwise OR operator sets a bit to 1 if at least one of the corresponding bits is 1. If both bits are 0, the result is 0.

Using the same example of 5 (0101) and 3 (0011), applying OR results in 0111, which is 7 in decimal. This operation effectively combines the bits of both numbers.

Bitwise OR is often used to set specific bits in a number without affecting others. It is useful in scenarios such as enabling flags or combining multiple binary states.

Bitwise XOR (^)

The XOR (exclusive OR) operator produces a result of 1 if the corresponding bits are different and 0 if they are the same. This unique behavior makes XOR particularly interesting and useful.

For example, 5 (0101) XOR 3 (0011) results in 0110, which is 6 in decimal. XOR has several special properties that make it valuable in algorithms and interview questions.

One of the most important properties is that any number XORed with itself results in 0, and any number XORed with 0 remains unchanged. These properties are often used in problems such as finding unique elements in arrays or swapping values without using a temporary variable.

Bitwise Complement (~)

The bitwise complement operator inverts all bits of a number. Every 0 becomes 1, and every 1 becomes 0. However, the result is not simply the negative of the number—it follows the rules of two’s complement representation.

For example, if you take the number 5 (00000101 in binary) and apply the complement operator, you get 11111010. In Java’s signed integer representation, this corresponds to -6.

This happens because the complement of a number is equal to the negative of that number minus one. In other words, ~x = -(x + 1). Understanding this relationship is crucial for correctly interpreting complement results.

Left Shift (<<)

The left shift operator moves bits to the left by a specified number of positions. As bits shift left, zeros are filled into the rightmost positions.

For example, shifting 5 (0101) one position to the left results in 1010, which is 10 in decimal. Each left shift effectively multiplies the number by 2.

This operator is commonly used for fast multiplication and performance optimization. Instead of using arithmetic multiplication, shifting can achieve the same result more efficiently at the binary level.

Right Shift (>>)

The signed right shift operator moves bits to the right while preserving the sign of the number. This means that the leftmost bit, known as the sign bit, is replicated to maintain the number’s sign.

For example, shifting -8 to the right results in -4. The sign bit ensures that the result remains negative.

Right shift is often used for dividing numbers by powers of two, similar to how left shift is used for multiplication.

Unsigned Right Shift (>>>)

The unsigned right shift operator also shifts bits to the right, but unlike the signed version, it fills the leftmost bits with zeros regardless of the sign.

This means that even negative numbers can become large positive numbers after an unsigned shift. This behavior is useful in specific low-level operations where the sign should not be preserved.

Unsigned right shift is less commonly used but is important for understanding how binary data is manipulated in systems programming.

Bitwise vs Logical Operators

A common source of confusion for beginners is the difference between bitwise and logical operators. While symbols such as & and | can be used in both contexts, their behavior is different.

Bitwise operators evaluate both operands fully and operate at the bit level. Logical operators such as && and || work with boolean expressions and use short-circuit evaluation, meaning they may skip evaluating the second operand.

Understanding this distinction is critical to avoid unexpected behavior, especially in conditional statements.

Real-World Use Cases

Although bitwise operators may seem abstract, they have practical applications in several domains. They are widely used in systems programming, where performance and memory efficiency are critical.

One common use case is managing flags and permissions. Each bit in a number can represent a specific permission, allowing multiple states to be stored in a single variable.

Bit masking is another important application, where specific bits are extracted or modified without affecting others. This technique is used in networking, encryption, and hardware-level programming.

Bitwise operations are also used in performance-critical algorithms, where traditional arithmetic operations may be too slow.

Common Mistakes

Despite their power, bitwise operators are often misused by beginners. One common mistake is confusing bitwise operators with logical operators, leading to incorrect results in conditional statements.

Another issue is ignoring the behavior of the sign bit, especially when using shift operators. This can result in unexpected negative values or incorrect calculations.

Overusing bitwise operators in business logic is also a mistake. While they are efficient, they can reduce code readability and make maintenance more difficult.

Understanding when to use bitwise operators—and when not to—is just as important as understanding how they work.

Interview Perspective

Bitwise operators are a favorite topic in technical interviews because they test fundamental understanding rather than memorization. Interviewers often use them to evaluate problem-solving skills and knowledge of binary operations.

A concise answer would describe bitwise operators as tools for performing operations on individual bits of integer data types. A more detailed explanation would include examples, use cases, and differences from logical operators.

Demonstrating familiarity with concepts such as XOR properties, shift operations, and masking techniques can significantly strengthen your interview performance.

Conclusion

Bitwise operators in Java may appear low-level and complex at first, but they provide powerful capabilities that are essential for certain types of programming. By allowing direct manipulation of binary data, they enable efficient operations that are not possible with standard arithmetic alone.

While they are not commonly used in everyday application development, understanding bitwise operators deepens your knowledge of how computers work and prepares you for advanced programming challenges.

The key is to approach them with clarity and purpose. Use them when they provide clear benefits, such as performance optimization or low-level control, and avoid them when they make code unnecessarily complex.

Mastering bitwise operators is not just about learning syntax—it is about developing a deeper understanding of computation itself.

1. Bitwise AND (&)

int a = 6;   // 110
int b = 3;   // 011
System.out.println(a & b); // 010 -> 2

Explanation

	• Each bit is 1 only if both bits are 1.

2. Bitwise OR (|)

int a = 4;   // 100
int b = 3;   // 011
System.out.println(a | b); // 111 -> 7

Explanation

	• Each bit is 1 if any bit is 1.

3. Bitwise XOR (^)

int a = 5;   // 101
int b = 3;   // 011
System.out.println(a ^ b); // 110 -> 6

Explanation

	• Each bit is 1 if bits are different.

4. Bitwise NOT (~)

int a = 5;
System.out.println(~a); // -6

Explanation

	• Flips all bits.
	• Formula: ~x = -(x + 1).

5. Left Shift (<<)

int a = 3;   // 0011
System.out.println(a << 2); // 1100 -> 12

Explanation

	• Shifts bits left.
	• Multiplies by 2^n.

6. Signed Right Shift (>>) – Positive

int a = 8;   // 1000
System.out.println(a >> 2); // 0010 -> 2

Explanation

	• Shifts right, preserving the sign bit.

7. Signed Right Shift (>>) – Negative

int a = -8;
System.out.println(a >> 1);

Explanation

	• Leftmost bits filled with 1 (sign extension).
	• Result remains negative.

8. Unsigned Right Shift (>>>)

int a = -8;
System.out.println(a >>> 1);

Explanation

	• Leftmost bits filled with 0.
	• Produces a large positive number.

9. Shift Count Masking (Important)

int a = 1;
System.out.println(a << 33);

Explanation

	• For int, shift count uses lower 5 bits.
	• 33 becomes 1 → effectively a << 1.

10. Bitwise AND with boolean (Non–Short-Circuit)

boolean x = false;
boolean y = true;
System.out.println(x & y);

Explanation

	• Evaluates both operands.
	• Not short-circuiting.

11. Bitwise OR with boolean (Non–Short-Circuit)

boolean x = true;
boolean y = false;
System.out.println(x | y);

Explanation

	• Both sides evaluated.
	• Differs from ||.

12. XOR with boolean

boolean a = true;
boolean b = false;
System.out.println(a ^ b); // true

Explanation

	• True only if exactly one operand is true.

13. Toggling a Value Using XOR

int a = 10;
a = a ^ 1;
System.out.println(a);

Explanation

	• XOR with 1 toggles the least significant bit.

14. Swapping Two Numbers Using XOR (Interview Classic)

int a = 5;
int b = 7;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println(a + " " + b);

Explanation

	• Swaps without a temporary variable.
	• Avoid in production for readability.

15. Checking Even/Odd Using AND

int n = 10;
System.out.println((n & 1) == 0);

Explanation

	• Last bit 0 → even, 1 → odd.
	• Faster than % 2.

16. Bit Masking (Read a Flag)

int flags = 0b1010;
int READ = 0b0010;
System.out.println((flags & READ) != 0);

Explanation

	• AND with mask checks if a specific bit is set.

17. Setting a Bit (OR)

int flags = 0b1000;
int WRITE = 0b0010;
flags |= WRITE;
System.out.println(Integer.toBinaryString(flags));

Explanation

	• OR sets a bit without affecting others.

18. Clearing a Bit (AND + NOT)

int flags = 0b1010;
int WRITE = 0b0010;
flags &= ~WRITE;
System.out.println(Integer.toBinaryString(flags));

Explanation

	• Clears a specific bit using masking.

19. Toggling a Bit (XOR)

int flags = 0b1010;
int EXECUTE = 0b1000;
flags ^= EXECUTE;
System.out.println(Integer.toBinaryString(flags));

Explanation

	• XOR flips the target bit.

20. Combining Shifts and OR (Pack Values)

int high = 0x12;
int low  = 0x34;
int packed = (high << 8) | low;
System.out.println(Integer.toHexString(packed));

Explanation

	• Packs two bytes into one int.
	• Common in protocols.

21. Extracting Packed Values

int packed = 0x1234;
int high = (packed >> 8) & 0xFF;
int low  = packed & 0xFF;
System.out.println(high + " " + low);

Explanation

	• Shifts + masks to extract parts.

22. Bitwise Operators with byte (Promotion)

byte a = 10;
byte b = 3;
// byte c = a & b; // compile-time error
byte c = (byte) (a & b);
System.out.println(c);

Explanation

	• Operands promoted to int.
	• Cast required to assign back.

23. Shift with char

char c = 'A'; // 65
System.out.println(c << 1);

Explanation

	• char promoted to int.
	• Arithmetic on Unicode value.

24. Bitwise vs Logical Operators (Key Difference)

int x = 0;
if (x != 0 & (10 / x) > 1) {
System.out.println("Risky");
}

Explanation

	• & evaluates both sides → exception risk.
	• Prefer && for conditions.

25. Interview Summary Example

int a = 6;   // 110
int b = 3;   // 011
System.out.println(a & b);   // 2
System.out.println(a | b);   // 7
System.out.println(a ^ b);   // 5
System.out.println(~a);      // -7
System.out.println(a << 1);  // 12
System.out.println(a >> 1);  // 3

Explanation

	• Covers AND, OR, XOR, NOT, shifts.
	• Common output-based interview question.