Arithmetic Expressions & Operator Precedence
How Java decides what to compute first, what happens when types mix, and the concat trap that prints wrong answers
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 right —
10 - 3 - 2is(10 - 3) - 2 = 5, not10 - (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:
a / b— both ints →2(integer division, truncated)2 + 3.0— int + double →2promoted to2.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.
System.out.println(7 / 2 * 1.0) print?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 + 2 → 3 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. Useprintfwhen you need formatted decimal output.
System.out.println("a=" + 2 + 3 + " b=" + (4 + 5)) print?"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, not5 - Type promotion is per-operation:
10 / 4 + 3.0 = 5.0, not5.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.