java-foundations 18 min read

Arithmetic Expressions & Operator Precedence

How Java decides what to compute first, what happens when types mix, and the concat trap that prints wrong answers

Reading: Reges & Stepp: Ch. 2 §2.2–2.4

Part 1 — Operator Precedence

Java evaluates arithmetic with the same rules you learned in math — but with one extra operator (%) and one extra trap (String +).

The Rules

Level Operators Associativity Example
1 (highest) () Innermost first (3 + 4) * 2 = 14
2 * / % Left → Right 10 / 2 * 3 = 15
3 (lowest) + - Left → Right 10 - 3 + 2 = 9

Two rules govern everything:

  • Higher level runs first*, /, % before +, -
  • Tie? Go left to right10 - 3 - 2 is (10 - 3) - 2 = 5, not 10 - (3 - 2) = 9

Tracing through expressions

System.out.println(2 + 3 * 4);     // 14  — (* before +): 2 + 12
System.out.println((2 + 3) * 4);   // 20  — parens override
System.out.println(10 - 3 - 2);    // 5   — left-to-right: (10-3)-2
System.out.println(10 % 4 + 1);    // 3   — (% before +): 2 + 1
System.out.println(10 / 4 * 2);    // 4   — left-to-right: (10/4)*2 = 2*2

The last one catches people. 10 / 4 * 2: / and * are the same level, so left-to-right applies. 10 / 4 = 2 (integer truncation), then 2 * 2 = 4. If you wanted 10 / (4 * 2), you need parentheses.

Practice: predict these by hand, then verify
5 + 3 * 2          // 11   (* first: 5 + 6)
(5 + 3) * 2        // 16   (parens: 8 * 2)
10 - 3 * 2 + 1     // 5    (* first: 10 - 6 + 1 = 5)
10 % 3 + 2 * 4     // 9    (% and *: 1 + 8)
100 / 10 / 2       // 5    (left-to-right: 10 / 2)

When in doubt, add parentheses

Extra parentheses cost nothing — the compiler ignores them. They prevent bugs and make intent explicit:

// A reader must mentally apply precedence rules to follow this
double result = salary + bonus * taxRate / 100 - deductions;

// This version is immediately readable
double result = salary + ((bonus * taxRate) / 100) - deductions;

Both lines compute the same result. The second one communicates it.


Part 2 — Mixed-Type Promotion

What happens when int and double appear in the same expression?

Rule: Java promotes int to double for that specific operation. Promotion is per-operation, not per-expression.

int a = 10;
int b = 4;

System.out.println(a / b);            // 2    — int/int → truncates
System.out.println(a / b + 3.0);      // 5.0  — (2) + 3.0: int 2 promoted to 2.0
System.out.println((double) a / b);   // 2.5  — 10.0/4: a promoted before dividing

Trace the second line step by step:

  1. a / b — both ints → 2 (integer division, truncated)
  2. 2 + 3.0 — int + double → 2 promoted to 2.0 → result: 5.0

The 3.0 cannot undo the truncation that already happened. To get 2.5, cast before the division: (double) a / b + 3.0.

Predict Before You Run
What does System.out.println(7 / 2 * 1.0) print?
A3.5
B3.0
C3
D3.50
Answer: B. Left-to-right at the same precedence level: 7 / 2 first — both ints → 3 (truncated). Then 3 * 1.0 — int × double → 3.0. The truncation happened before the double arrived. To get 3.5, cast first: (double) 7 / 2 * 1.0.

Part 3 — The String Concatenation Trap

Java uses + for two completely different things:

  • Both sides are numbers → arithmetic (3 + 4 = 7)
  • Either side is a String → joining ("x=" + 4 = "x=4")

Java evaluates left to right. Once a String appears, everything to its right joins:

System.out.println("Total: " + 1 + 2);    // "Total: 12"  ← bug
System.out.println("Total: " + (1 + 2));  // "Total: 3"   ← correct
System.out.println(1 + 2 + " total");     // "3 total"    ← correct

Line 1: "Total: " + 1"Total: 1" (joining). Then "Total: 1" + 2"Total: 12" (still joining). No addition.

Line 2: (1 + 2)3 first (parens, arithmetic). Then "Total: " + 3"Total: 3".

Line 3: 1 + 23 first (no String yet). Then 3 + " total""3 total".

The fix

If you want arithmetic in a print statement, either compute first or use parentheses:

int miles = 350;
int mpg = 28;

// WRONG — concat trap, prints "Gallons: 350" + 28 → "Gallons: 35028"
System.out.println("Gallons: " + miles + mpg);

// RIGHT option 1 — compute first
double gallons = (double) miles / mpg;
System.out.println("Gallons: " + gallons);

// RIGHT option 2 — parentheses
System.out.println("Gallons: " + ((double) miles / mpg));

Rule: When arithmetic and a String appear in the same println, compute into a variable first. Use printf when you need formatted decimal output.

Predict Before You Run
What does System.out.println("a=" + 2 + 3 + " b=" + (4 + 5)) print?
Aa=5 b=9
Ba=23 b=9
Ca=23 b=45
DCompile error
Answer: B. Left-to-right: "a=" + 2 = "a=2". Then "a=2" + 3 = "a=23". Then "a=23" + " b=" = "a=23 b=". Then (4+5) = 9 (parens protect the arithmetic). Final: "a=23 b=9".

What You Learned

  • *, /, % have higher precedence than +, - — same as PEMDAS
  • Operators at the same level evaluate left to right — 10 / 4 * 2 = 4, not 5
  • Type promotion is per-operation: 10 / 4 + 3.0 = 5.0, not 5.5 — the truncation already happened
  • String + joins left-to-right: once a String appears, following numbers join rather than add
  • Fix the concat trap with parentheses or by computing first and storing in a variable
  • Parentheses never hurt — use them whenever an expression might be misread

What Comes Next

Next: compound assignment and increment (+=, -=, *=, ++, --). These are the shorthand operators that make accumulation patterns clean — and the building blocks of every loop you will write once loops arrive.