AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

02 Types & operators / Lesson 6 of 7

Operators and precedence

14 min · +10 XP · 2 exercises · 5 questions

Read the text14 min
Exercises0/2
Quiz5 q

Quick answer

What is operator precedence in Java?

Operator precedence in Java decides how an expression without parentheses is grouped: postfix and unary operators bind tightest, then * / %, then + -, shifts, comparisons, equality, the bitwise operators, &&, ||, the ternary and finally assignment. Operands are always evaluated left to right. Bitwise operators bind looser than comparisons, so write (x & 1) == 1.

Java's operators are C's, with the sharp edges filed off: no pointer arithmetic, no comma operator, no implicit truthiness, and a specified evaluation order. This lesson covers each family with the details interviewers probe — short-circuiting, bitwise versus logical, the three shifts, and the precedence table you should know rather than guess.

Arithmetic

+ - * / %, unary - and +, ++ and --. Integer versus floating behaviour was covered in the last two lessons. + is also string concatenation when either operand is a String. There is no exponent operator; Math.pow or a loop.

Relational and equality

< <= > >= work on numeric types (and char). == and != work on everything — but for references they compare identity (is it the same object?), never contents. "hi" == "hi" may be true (string pool) or false (built at run time); equals is the content comparison. Module 3 and Module 8 make this precise; for now: == on objects is almost always wrong unless you mean "the same object".

Comparisons produce boolean, and boolean is not a number, so a < b < c does not compile (the first comparison yields a boolean, which < cannot take).

Logical: &&, ||, ! — and &, |, ^

if (list != null && !list.isEmpty()) …     // safe: right side not evaluated when left is false
if (x > 0 || expensiveCheck()) …           // expensiveCheck runs only when x <= 0

&& and || short-circuit: the right operand is evaluated only if needed. This is a correctness tool, not just an optimisation — it is what makes obj != null && obj.method() safe.

&, | and ^ on booleans are the non-short-circuit logical operators: both sides always evaluate. Use them when the right side has a side effect you want regardless (rare), or for XOR, which has no ^^ form. On integers the same three symbols are bitwise (below).

! negates a boolean. There is no not keyword.

Bitwise and shifts

On integral types (int, long; smaller types promote to int):

OpMeaningExample (8-bit view)
&AND0b1100 & 0b1010 → 0b1000
|OR0b1100 | 0b1010 → 0b1110
^XOR0b1100 ^ 0b1010 → 0b0110
~NOT (one's complement)~5 → -6 (all bits flipped; ~x == -x - 1)
<<shift left, fill with 01 << 4 → 16
>>arithmetic shift right, fill with the sign bit-16 >> 2 → -4
>>>logical shift right, fill with 0-16 >>> 28 → 15

Shift facts that come up:

  • The shift distance is taken modulo the width: 1 << 32 is 1 (not 0) for an int, because 32 & 31 = 0. For long, modulo 64.
  • >> keeps the sign, so it divides by 2ⁿ rounding toward negative infinity (-7 >> 1 is -4, while -7 / 2 is -3).
  • >>> exists because there are no unsigned types; it treats the value as unsigned bits. (a + b) >>> 1 is the overflow-safe midpoint.
  • x ^ x == 0, x ^ 0 == x, XOR is its own inverse — the basis of "find the single number" problems and swap-without-temp (a ^= b; b ^= a; a ^= b; — do not use in real code).
  • Masks: flags & FLAG_X tests, flags | FLAG_X sets, flags & ~FLAG_X clears, flags ^ FLAG_X toggles.
  • Integer.toBinaryString(x), Integer.bitCount, Integer.highestOneBit, Integer.numberOfTrailingZeros, Long.reverse — the toolkit in the wrapper classes.

Assignment and compound assignment

= is an expression that yields the assigned value, so a = b = c = 0 works (right-associative). The compound forms += -= *= /= %= &= |= ^= <<= >>= >>>= include the implicit cast to the left-hand type (previous lessons). x = x + 1 and x += 1 and x++ all do the same thing for an int; they differ for byte/short/char (only the last two compile without a cast).

The ternary ?:

int max = a > b ? a : b;
String label = count == 1 ? "item" : "items";

The only three-operand operator. Both branches must produce compatible types, and numeric promotion applies to them: true ? 1 : 2.0 is 1.0, and flag ? 'a' : 0 is a char because 0 is a constant that fits — but flag ? 'a' : someInt is an int. Nest it at most once; beyond that use if.

A famous trap:

Integer x = null;
int y = flag ? x : 0;   // NullPointerException when flag is true: x is unboxed

And the subtler one: Object o = true ? Integer.valueOf(1) : Double.valueOf(2.0); prints 1.0, because both wrapper operands are unboxed and promoted to double.

instanceof

obj instanceof Type is true if obj is non-null and is a Type (or subtype). Since Java 16 it can bind a variable: if (obj instanceof String s) { s.length(); }. Module 8.

Precedence, from tightest to loosest

LevelOperatorsAssociativity
1postfix x++ x--, member ., call (), index []left
2unary ++x --x +x -x ~ !, cast (type)right
3* / %left
4+ -left
5<< >> >>>left
6< <= > >= instanceofleft
7== !=left
8&left
9^left
10|left
11&&left
12||left
13?:right
14= += -= …right

The rows that cause bugs:

  • Bitwise binds looser than comparison: x & 1 == 1 parses as x & (1 == 1), a compile error (int & boolean). Write (x & 1) == 1.
  • Shift binds looser than +: 1 << 2 + 3 is 1 << 5 = 32, not 7.
  • Cast binds tighter than binary operators: (double) a / b casts a only.
  • && before ||: a || b && c is a || (b && c).

When in doubt, parenthesise. Reviewers never complain about clarifying parentheses.

Evaluation order

Operands are evaluated left to right, fully, before the operator applies — and this is guaranteed by the language, unlike C/C++. f() + g() calls f first. Method arguments are evaluated left to right. a[i] = i++ stores into the old index. Predictable, but still write code that does not depend on it.

Operators Java does not have

No **, no <> for not-equal, no -> for member access (-> is the lambda arrow), no comma operator, no sizeof, no pointer */&, no operator overloading (+ on strings is the one built-in exception). BigInteger arithmetic is method calls because of this.

Interview angle

  • "Difference between & and &&?" && short-circuits; & evaluates both and is also bitwise on integers.
  • ">> vs >>>?" Arithmetic (sign-filling) vs logical (zero-filling) right shift.
  • "1 << 32 for an int?" 1 — the distance is masked to 5 bits.
  • "Result of ~7?" −8.

Key takeaways

  • &&/|| short-circuit; &/|/^ do not, and double as bitwise operators.
  • >> keeps the sign, >>> does not; shift distances wrap at the type's width.
  • Bitwise operators bind looser than comparisons — parenthesise masks.
  • ?: unboxes and promotes both branches; a null wrapper there throws.
  • Evaluation is strictly left to right.

Common questions

What is the difference between & and && in Java?

&& short-circuits: it skips the right operand when the left is false, which makes obj != null && obj.isReady() safe. & on booleans always evaluates both sides, and on integers it is bitwise AND. || and | differ in the same way.

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

>> is the arithmetic right shift: it fills with the sign bit, so negative numbers stay negative and -16 >> 2 is -4. >>> is the logical right shift: it fills with zeros, treating the bits as unsigned, which is why (low + high) >>> 1 is an overflow-safe midpoint.

Why is 1 << 32 equal to 1 for an int in Java?

The shift distance is taken modulo the type's width: only the low 5 bits count for an int, and 32 masked to 5 bits is 0, so 1 << 32 shifts by nothing. For a long the distance is taken modulo 64.

Why does a ternary operator throw NullPointerException in Java?

When one branch is a wrapper such as Integer and the other a primitive, the result is unboxed, so int y = flag ? x : 0; throws when x is null. Both branches are also promoted to a common type, which is why true ? 1 : 2.0 is 1.0.

Does Java have an exponent operator?

No. Java has no ** or other power operator; use Math.pow(base, exponent), which returns a double, a loop for integer powers, or 1 << n for powers of two. Java has no operator overloading either, which is why BigInteger arithmetic is method calls.

Mark as readSign in to keep your place.

Java Operators and Precedence: Bitwise, Shift and Ternary

Java operators explained: why && and || short-circuit, how the three shift operators differ, ternary traps and the precedence rules that cause bugs.

  • Course: Java study plan
  • Module: Types & operators
  • Kind: Lesson
  • Reading time: 14 min
  • Runtime: OpenJDK 18

What is operator precedence in Java?

Operator precedence in Java decides how an expression without parentheses is grouped: postfix and unary operators bind tightest, then * / %, then + -, shifts, comparisons, equality, the bitwise operators, &&, ||, the ternary and finally assignment. Operands are always evaluated left to right. Bitwise operators bind looser than comparisons, so write (x & 1) == 1.

Lesson

Java's operators are C's, with the sharp edges filed off: no pointer arithmetic, no comma operator, no implicit truthiness, and a specified evaluation order. This lesson covers each family with the details interviewers probe — short-circuiting, bitwise versus logical, the three shifts, and the precedence table you should know rather than guess.

Arithmetic

+ - * / %, unary - and +, ++ and --. Integer versus floating behaviour was covered in the last two lessons. + is also string concatenation when either operand is a String. There is no exponent operator; Math.pow or a loop.

Relational and equality

< <= > >= work on numeric types (and char). == and != work on everything — but for references they compare identity (is it the same object?), never contents. "hi" == "hi" may be true (string pool) or false (built at run time); equals is the content comparison. Module 3 and Module 8 make this precise; for now: == on objects is almost always wrong unless you mean "the same object".

Comparisons produce boolean, and boolean is not a number, so a < b < c does not compile (the first comparison yields a boolean, which < cannot take).

Logical: &&, ||, ! — and &, |, ^

if (list != null && !list.isEmpty()) …     // safe: right side not evaluated when left is false
if (x > 0 || expensiveCheck()) …           // expensiveCheck runs only when x <= 0

&& and || short-circuit: the right operand is evaluated only if needed. This is a correctness tool, not just an optimisation — it is what makes obj != null && obj.method() safe.

&, | and ^ on booleans are the non-short-circuit logical operators: both sides always evaluate. Use them when the right side has a side effect you want regardless (rare), or for XOR, which has no ^^ form. On integers the same three symbols are bitwise (below).

! negates a boolean. There is no not keyword.

Bitwise and shifts

On integral types (int, long; smaller types promote to int):

OpMeaningExample (8-bit view)
&AND0b1100 & 0b1010 → 0b1000
|OR0b1100 | 0b1010 → 0b1110
^XOR0b1100 ^ 0b1010 → 0b0110
~NOT (one's complement)~5 → -6 (all bits flipped; ~x == -x - 1)
<<shift left, fill with 01 << 4 → 16
>>arithmetic shift right, fill with the sign bit-16 >> 2 → -4
>>>logical shift right, fill with 0-16 >>> 28 → 15

Shift facts that come up:

  • The shift distance is taken modulo the width: 1 << 32 is 1 (not 0) for an int, because 32 & 31 = 0. For long, modulo 64.
  • >> keeps the sign, so it divides by 2ⁿ rounding toward negative infinity (-7 >> 1 is -4, while -7 / 2 is -3).
  • >>> exists because there are no unsigned types; it treats the value as unsigned bits. (a + b) >>> 1 is the overflow-safe midpoint.
  • x ^ x == 0, x ^ 0 == x, XOR is its own inverse — the basis of "find the single number" problems and swap-without-temp (a ^= b; b ^= a; a ^= b; — do not use in real code).
  • Masks: flags & FLAG_X tests, flags | FLAG_X sets, flags & ~FLAG_X clears, flags ^ FLAG_X toggles.
  • Integer.toBinaryString(x), Integer.bitCount, Integer.highestOneBit, Integer.numberOfTrailingZeros, Long.reverse — the toolkit in the wrapper classes.

Assignment and compound assignment

= is an expression that yields the assigned value, so a = b = c = 0 works (right-associative). The compound forms += -= *= /= %= &= |= ^= <<= >>= >>>= include the implicit cast to the left-hand type (previous lessons). x = x + 1 and x += 1 and x++ all do the same thing for an int; they differ for byte/short/char (only the last two compile without a cast).

The ternary ?:

int max = a > b ? a : b;
String label = count == 1 ? "item" : "items";

The only three-operand operator. Both branches must produce compatible types, and numeric promotion applies to them: true ? 1 : 2.0 is 1.0, and flag ? 'a' : 0 is a char because 0 is a constant that fits — but flag ? 'a' : someInt is an int. Nest it at most once; beyond that use if.

A famous trap:

Integer x = null;
int y = flag ? x : 0;   // NullPointerException when flag is true: x is unboxed

And the subtler one: Object o = true ? Integer.valueOf(1) : Double.valueOf(2.0); prints 1.0, because both wrapper operands are unboxed and promoted to double.

instanceof

obj instanceof Type is true if obj is non-null and is a Type (or subtype). Since Java 16 it can bind a variable: if (obj instanceof String s) { s.length(); }. Module 8.

Precedence, from tightest to loosest

LevelOperatorsAssociativity
1postfix x++ x--, member ., call (), index []left
2unary ++x --x +x -x ~ !, cast (type)right
3* / %left
4+ -left
5<< >> >>>left
6< <= > >= instanceofleft
7== !=left
8&left
9^left
10|left
11&&left
12||left
13?:right
14= += -= …right

The rows that cause bugs:

  • Bitwise binds looser than comparison: x & 1 == 1 parses as x & (1 == 1), a compile error (int & boolean). Write (x & 1) == 1.
  • Shift binds looser than +: 1 << 2 + 3 is 1 << 5 = 32, not 7.
  • Cast binds tighter than binary operators: (double) a / b casts a only.
  • && before ||: a || b && c is a || (b && c).

When in doubt, parenthesise. Reviewers never complain about clarifying parentheses.

Evaluation order

Operands are evaluated left to right, fully, before the operator applies — and this is guaranteed by the language, unlike C/C++. f() + g() calls f first. Method arguments are evaluated left to right. a[i] = i++ stores into the old index. Predictable, but still write code that does not depend on it.

Operators Java does not have

No **, no <> for not-equal, no -> for member access (-> is the lambda arrow), no comma operator, no sizeof, no pointer */&, no operator overloading (+ on strings is the one built-in exception). BigInteger arithmetic is method calls because of this.

Interview angle

  • "Difference between & and &&?" && short-circuits; & evaluates both and is also bitwise on integers.
  • ">> vs >>>?" Arithmetic (sign-filling) vs logical (zero-filling) right shift.
  • "1 << 32 for an int?" 1 — the distance is masked to 5 bits.
  • "Result of ~7?" −8.

Key takeaways

  • &&/|| short-circuit; &/|/^ do not, and double as bitwise operators.
  • >> keeps the sign, >>> does not; shift distances wrap at the type's width.
  • Bitwise operators bind looser than comparisons — parenthesise masks.
  • ?: unboxes and promotes both branches; a null wrapper there throws.
  • Evaluation is strictly left to right.

Common questions

What is the difference between & and && in Java?

&& short-circuits: it skips the right operand when the left is false, which makes obj != null && obj.isReady() safe. & on booleans always evaluates both sides, and on integers it is bitwise AND. || and | differ in the same way.

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

>> is the arithmetic right shift: it fills with the sign bit, so negative numbers stay negative and -16 >> 2 is -4. >>> is the logical right shift: it fills with zeros, treating the bits as unsigned, which is why (low + high) >>> 1 is an overflow-safe midpoint.

Why is 1 << 32 equal to 1 for an int in Java?

The shift distance is taken modulo the type's width: only the low 5 bits count for an int, and 32 masked to 5 bits is 0, so 1 << 32 shifts by nothing. For a long the distance is taken modulo 64.

Why does a ternary operator throw NullPointerException in Java?

When one branch is a wrapper such as Integer and the other a primitive, the result is unboxed, so int y = flag ? x : 0; throws when x is null. Both branches are also promoted to a common type, which is why true ? 1 : 2.0 is 1.0.

Does Java have an exponent operator?

No. Java has no ** or other power operator; use Math.pow(base, exponent), which returns a double, a loop for integer powers, or 1 << n for powers of two. Java has no operator overloading either, which is why BigInteger arithmetic is method calls.

Exercises

Bit report

Read an integer n, then n integers. For each value x print one line:

<x> bin=<binary> ones=<count> parity=<even|odd> half=<x >> 1> logical=<x >>> 1> pow2=<yes|no>

where binary is Integer.toBinaryString(x), ones is Integer.bitCount(x), parity comes from x & 1, and pow2 is yes when x > 0 and (x & (x - 1)) == 0.

Input: n, then n integers. Output: n lines.

Predict the operators

Read an integer k and k expression ids in 1..12. For each print <id>: <value>, writing the expression literally in Java (no hard-coded answers). Predict each before running.

idexpressionidexpression
15 / 2 * 2.07int i = 5; i++ + ++i
27 % -38`truefalse && false`
3`1 << 31`9(int) 'A' + 1
46 & 3 ^ 11010 - 2 - 3
5-7 >> 1112 + 3 * 4 % 5
6-7 >>> 2812int x = 5; x += x++ * 2; x

Example: 3 then 1 3 7 →

1: 4.0
3: 9
7: 12

In this module: Types & operators

← Floating point — doubles, precision and BigDecimal · Wrapper classes and autoboxing →