Expressions and Operators
Precedence, type promotion, casting, and the concatenation trap
After this lesson, you will be able to:
- Evaluate arithmetic expressions using Java’s precedence rules
- Predict the result of mixed-type expressions using type promotion
- Use explicit casts to convert between
intanddouble - Explain why
0.1 + 0.2 != 0.3and use epsilon comparison - Apply compound assignment (
+=) and increment (++) operators - Avoid the string concatenation trap with parentheses
Why Does 10 / 4 Give 2?
Try this in Python:
print(10 / 4) # 2.5
Now try it in Java:
System.out.println(10 / 4); // 2
Where did the .5 go? Java sees two int operands, performs integer division, and truncates the decimal. This one behavior causes more bugs in student code than almost anything else in the course. Today you learn exactly how Java evaluates every expression — what order, what types, and what traps to avoid.
From CSCD 110: In Python,
/always gives a float and//gives integer division. In Java,/does integer division when both operands areint. There is no//operator. To get decimal division, at least one operand must bedouble.
Operator Precedence: PEMDAS with a Twist
Java evaluates arithmetic using the same rules you learned in math class:
| Level | Operators | Associativity | Example |
|---|---|---|---|
| 1 (highest) | () |
Inner to outer | (3 + 4) * 2 → 14 |
| 2 | *, /, % |
Left to right | 10 / 2 * 3 → 15 |
| 3 (lowest) | +, - |
Left to right | 10 - 3 + 2 → 9 |
Rule 1: Higher-precedence operators go first. Rule 2: Same-level operators go left to right. Rule 3: Parentheses override everything.
Let’s trace 2 + 3 * 4 - 1:
*has higher precedence:3 * 4→12. Expression becomes2 + 12 - 1.+and-are same level, left to right:2 + 12→14.14 - 1→ 13.
Key Insight: When in doubt, add parentheses.
a + ((b * c) / d) - eis longer thana + b * c / d - ebut instantly readable. Professional code favors clarity over cleverness.
What is the value of 10 % 3 + 2 * 4?
Mixed Types: Type Promotion
When int meets double in an expression, Java automatically promotes the int to double before the operation. The result is always double:
System.out.println(3 + 2.5); // 5.5 (int promoted to double)
System.out.println(10 / 4); // 2 (both int → integer division)
System.out.println(10 / 4.0); // 2.5 (int promoted → real division)
System.out.println(10.0 / 4); // 2.5 (int promoted → real division)
Promotion follows one direction: byte → short → int → long → float → double. You never lose data going right.
The critical detail is that promotion happens per operation, not per expression:
// Step-by-step: 10 / 4 + 3.0
// Step 1: 10 / 4 → both int → integer division → 2
// Step 2: 2 + 3.0 → int promoted to 2.0 → 5.0
System.out.println(10 / 4 + 3.0); // 5.0 (NOT 5.5!)
Common Pitfall: Integer division happens before promotion kicks in. In
10 / 4 + 3.0, the division10 / 4uses twointoperands and produces2. The truncation damage is already done by the time3.0enters the picture.
Explicit Casting
Sometimes you need to force a type conversion yourself. An explicit cast uses the target type in parentheses:
double gpa = 3.87;
int truncated = (int) gpa; // 3 (decimal part chopped off)
int total = 255;
int count = 3;
double average = (double) total / count; // 85.0 (correct!)
There are two directions:
Widening (int → double) is safe and automatic — no data lost.
Narrowing (double → int) requires an explicit cast because it truncates — the decimal part is dropped, not rounded:
(int) 3.99 // 3 — truncated, NOT rounded
(int) -2.7 // -2 — toward zero
If you want rounding, use Math.round():
long rounded = Math.round(3.99); // 4L
int roundedInt = (int) Math.round(3.99); // 4
The Trick: The cast operator has higher precedence than arithmetic.
(double) x / ycastsxfirst, then divides (giving a decimal result).(double) (x / y)divides first (integer division), then casts — still wrong. Cast before the operation.
Given int x = 7, y = 2;, what is the value of (double) (x / y)?
The String Concatenation Trap
The + operator does double duty: addition with numbers, concatenation with strings. Since + evaluates left to right, the position of a String determines what is arithmetic and what is concatenation:
System.out.println("Sum: " + 2 + 3); // "Sum: 23"
System.out.println(2 + 3 + " is sum"); // "5 is sum"
System.out.println("Sum: " + (2 + 3)); // "Sum: 5"
Trace the first line: "Sum: " + 2 → String + int → "Sum: 2". Then "Sum: 2" + 3 → String + int → "Sum: 23".
Trace the second line: 2 + 3 → int + int → 5. Then 5 + " is sum" → int + String → "5 is sum".
The Trick: To force arithmetic before concatenation, wrap the math in parentheses:
// BAD: concatenation instead of addition System.out.println("Total: " + price + tax); // "Total: 1020" // GOOD: arithmetic first, then concatenation System.out.println("Total: " + (price + tax)); // "Total: 30"
System.out.println("Result: " + 2 + 3);
Compound Assignment and Increment
The most common things you do with variables are add to them, subtract from them, and count. Java provides shorthand:
| Shorthand | Equivalent | Meaning |
|---|---|---|
x += 3 |
x = x + 3 |
Add 3 to x |
x -= 5 |
x = x - 5 |
Subtract 5 from x |
x *= 2 |
x = x * 2 |
Multiply x by 2 |
x /= 4 |
x = x / 4 |
Divide x by 4 |
x %= 3 |
x = x % 3 |
Set x to x mod 3 |
x++ |
x = x + 1 |
Increment by 1 |
x-- |
x = x - 1 |
Decrement by 1 |
int total = 0;
total += 10; // total is now 10
total += 25; // total is now 35
total -= 5; // total is now 30
int count = 0;
count++; // count is now 1
count++; // count is now 2
There are two forms of increment: post (x++) returns the old value then increments, and pre (++x) increments first then returns the new value. As standalone statements, they are identical. The difference only matters inside larger expressions:
int x = 5;
int y = x++; // y is 5, x is 6 (post: use old, then increment)
int a = 5;
int b = ++a; // b is 6, a is 6 (pre: increment, then use new)
Best practice: use x++ and x-- as standalone statements. Avoid embedding them in complex expressions.
Common Pitfall: Never write
x = x++;— it does not incrementx. Post-increment returns the old value (5), then increments x to 6, then the assignment overwrites x back to 5. Just writex++;.
Floating-Point Precision
One more surprise:
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3); // false!
This is not a Java bug. Computers store numbers in binary, and 0.1 cannot be represented exactly in binary — just as 1/3 cannot be represented exactly in decimal (0.333…). This affects every programming language.
Never compare double values with ==. Use epsilon comparison:
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 1e-9;
if (Math.abs(a - b) < epsilon) {
System.out.println("Approximately equal"); // prints!
}
Three types, three comparison strategies:
| Type | Correct Comparison |
|---|---|
int |
a == b |
double |
Math.abs(a - b) < epsilon |
String |
a.equals(b) |
The Math Class
Java provides mathematical functions through the Math class (no import needed):
| Method | Returns | Example | Result |
|---|---|---|---|
Math.abs(x) |
same type | Math.abs(-7) |
7 |
Math.pow(b, e) |
double |
Math.pow(2, 10) |
1024.0 |
Math.sqrt(x) |
double |
Math.sqrt(25) |
5.0 |
Math.round(x) |
long |
Math.round(3.6) |
4L |
Math.max(a, b) |
same type | Math.max(5, 9) |
9 |
Math.min(a, b) |
same type | Math.min(5, 9) |
5 |
Since Math.pow() and Math.sqrt() return double, cast back to int when needed:
int squared = (int) Math.pow(5, 2); // 25
int rounded = (int) Math.round(4.6); // 5
The Trick: To round a
doubleto n decimal places, multiply by 10^n, round, then divide by 10^n:double price = 19.876; double rounded = Math.round(price * 100.0) / 100.0; // 19.88
Putting It Together: Pay Stub Calculator
Here is a complete program combining precedence, type promotion, casting, concatenation, and printf:
import java.util.Scanner;
public class PayStub {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.print("Enter hours worked: ");
double hours = scanner.nextDouble();
System.out.print("Enter hourly rate: ");
double rate = scanner.nextDouble();
double grossPay = hours * rate;
double taxRate = 0.15;
double tax = grossPay * taxRate;
double netPay = grossPay - tax;
System.out.println("=== Pay Stub for " + name + " ===");
System.out.println("Hours: " + hours + " @ $" + rate + "/hr");
System.out.printf("Gross Pay: $%.2f%n", grossPay);
System.out.printf("Tax (15%%): $%.2f%n", tax);
System.out.printf("Net Pay: $%.2f%n", netPay);
}
}
Sample output:
Enter your name: Alice
Enter hours worked: 40
Enter hourly rate: 18.50
=== Pay Stub for Alice ===
Hours: 40.0 @ $18.5/hr
Gross Pay: $740.00
Tax (15%): $111.00
Net Pay: $629.00
What does System.out.println("" + 10 + 20); print?
Formula Translation Practice
Translating math formulas into Java is where all these rules come together. Watch for integer division traps:
// Area of a triangle: A = (1/2) * b * h
double area = 0.5 * base * height; // 0.5 avoids integer division
// Celsius to Fahrenheit: F = (9/5) * C + 32
double fahrenheit = 9.0 / 5.0 * celsius + 32; // 9/5 would give 1!
// Average of three int scores
double average = (double) (s1 + s2 + s3) / 3; // cast sum before dividing
// Distance formula: d = sqrt((x2-x1)^2 + (y2-y1)^2)
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
Which Java expression correctly computes the average of three int variables a, b, and c as a decimal value?
Summary
Every computation in Java is an expression governed by precedence rules. Multiplication, division, and modulus go before addition and subtraction. Same-level operators evaluate left to right. Parentheses override everything.
When int meets double, the int is promoted — but promotion happens per operation, not per expression. 10 / 4 + 3.0 gives 5.0, not 5.5, because integer division already truncated.
Explicit casts let you convert between types: (double) sum / count for correct averages, (int) Math.round(x) for rounding. Casting truncates; Math.round() rounds.
Never compare double values with ==. Use epsilon comparison: Math.abs(a - b) < epsilon.
Compound assignment (+=, -=, *=) and increment (++, --) are shorthand for the most common operations. These become essential when you learn loops.
Next lesson: We take a closer look at String — Java’s most-used reference type. You will learn charAt(), substring(), equals() vs ==, and why strings behave differently from int and double.