How does Java resolve operator precedence and associativity, and what is the most common precedence bug you see in real code?
Java's rules are the standard ones you already know from C-family languages, with three named levels to remember. Multiplicative operators (multiplication, division, remainder) bind tightest. Additive operators come next. Then the shift, relational, equality, bitwise AND, bitwise XOR, bitwise OR, logical AND, logical OR, conditional and assignment operators in that order, with assignment loosest of all. Equality sits below relational, so a == b < c groups as a == (b < c). All the binary arithmetic operators are left-associative, and the conditional operator is right-associative.
The bug that actually reaches production is the logical one: && binds tighter than ||, so a || b && c is a || (b && c). Programmers who intend (a || b) && c get the other grouping, and because both forms compile cleanly the mistake ships.
public class Main {
public static void main(String[] args) {
int a = 5, b = 10;
boolean flag = false;
// && binds tighter than ||, so this is (a == 5) || ((b == 10) && flag)
boolean asWritten = a == 5 || b == 10 && flag;
boolean asIntended = (a == 5 || b == 10) && flag;
System.out.println("as written : " + asWritten);
System.out.println("as intended : " + asIntended);
// Shift versus addition: + binds tighter than >>
int value = -16;
System.out.println("-16 >> 2 : " + (value >> 2));
System.out.println("-16 >>> 2 : " + (value >>> 2));
System.out.println("1 + 2 << 3 : " + (1 + 2 << 3));
// Parenthesise when in doubt: it costs nothing.
boolean explicit = (a == 5 || b == 10) && flag;
System.out.println("explicit : " + explicit);
}
}Two more facts that interviewers like to hear. The conditional operator is the only ternary operator in Java and, unlike in C, it requires a result type on both branches that can be assigned to the expression type without an unboxing conversion, so mixing int and Integer in the two branches fails to compile. And compound assignment operators such as += are not simply sugar for x = x + y: the right-hand side is evaluated once, cast to the type of the variable, and the operation is performed in that type, which matters for byte arithmetic where b += 1 wraps but b = b + 1 does not compile without a cast.