Programs that only store values are not very useful. The moment you need to calculate a total, check whether a score passed a threshold, or combine several conditions, you are writing an expression — values joined by operators. Getting the right result is not just about knowing the symbols; it is about understanding precedence, type rules, and the classic traps that silently turn correct-looking code into wrong answers. This lesson covers the operators you will use in every Java program you write.
flowchart TD P1["() parentheses"] P2["! + - (unary)"] P3["* / %"] P4["+ -"] P5["< > <= >= == !="] P6["&&"] P7["||"] P8["= += -= etc."] P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8 style P1 fill:#1e293b,color:#fff style P8 fill:#1e293b,color:#fff
Arithmetic operators
Java provides the familiar math symbols: +, -, *, /, and % (modulo, the remainder after division). They work as you expect with int and double, but one rule catches every beginner: when both operands are integers, / drops the remainder. 10 / 3 is 3, not 3.33. The % operator gives you what / threw away — 10 % 3 is 1.
This is not a bug; it is how Java models integer arithmetic. If you need a decimal result, at least one operand must be a double: 10.0 / 3 gives 3.333.... Forgetting this is the source of countless off-by-one errors in loops and averages.
The power of modulo
The % operator has uses beyond remainders. It is how you check whether a number is even (x % 2 == 0), how you wrap an index around a circular buffer, and how you extract the last digit of a number (x % 10). Learning to reach for % naturally will save you from writing fragile conditional logic.
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 3;
System.out.println(a / b);
System.out.println(a % b);
System.out.println(a / 3.0);
}
}
Relational operators
These six operators compare two values and produce a boolean: == (equal), != (not equal), < (less than), > (greater than), <= (less than or equal), and >= (greater than or equal). They work on numbers, characters, and any ordered type.
A common mistake is using = (assignment) instead of == (comparison). Java will refuse to compile if (a = 5) when a is an int, because the condition expects a boolean. That compiler error is your friend — it catches the bug before the program runs.
You can chain relational results with logical operators to build complex conditions: if (score >= 80 && attendance > 0.75) is readable because each piece is small and explicit.
Logical operators
&& (and), || (or), and ! (not) combine boolean values. && is true only when both sides are true. || is true when at least one side is true. ! flips a single boolean.
These operators short-circuit: if the left side of && is false, the right side is skipped entirely. If the left side of || is true, the right side is skipped. That is not just a performance trick — it is a safety feature. if (list != null && list.size() > 0) works because the null check happens first; if it were false, the size check would never execute and could not throw a NullPointerException.
flowchart TD A["list != null"] -->|false| B["skip list.size()"] A -->|true| C["list.size() > 0"] C --> D["full condition evaluated"] B --> E["safe: no NullPointerException"] style A fill:#c2410c,color:#fff style B fill:#166534,color:#fff
public class Main {
public static void main(String[] args) {
int score = 85;
boolean bonus = true;
System.out.println(score >= 80 && bonus);
System.out.println(score < 60 || score > 90);
System.out.println(!(score == 85));
}
}
Increment, decrement, and compound assignment
++ and -- add or subtract one from a variable. When they sit before the variable (++x), the increment happens before the value is used in the larger expression. When they sit after (x++), the original value is used first, then the increment happens. This distinction matters in complex expressions, so most teams ban ++ inside other expressions and use it only as a standalone statement.
Compound assignment operators — +=, -=, *=, /=, %= — update a variable in place. x += 5 is shorthand for x = x + 5. They save typing and make intent clearer, but they still follow the same type rules: x /= 2 on an int truncates the result.
Compound assignments also work with compatible types: x += 2.5 where x is an int compiles, because the operator includes an implicit cast — though it still truncates the result. Relying on that hidden cast is considered poor style; if you need decimals, use a double.
Mixed-type arithmetic and casting
When an int and a double appear in the same expression, Java promotes the int to double automatically, so 5 + 2.5 becomes 7.5. This is called widening conversion, and it is safe because no information is lost.
The reverse direction — putting a double into an int — requires an explicit cast: (int) 7.9 gives 7. The decimal is truncated, not rounded. Casts are how you tell the compiler, 'I know this looks dangerous, and I accept the result.' Use them sparingly and always check whether truncation is what you actually want. You will see this pattern constantly when mixing pixel coordinates (int) with scale factors (double) in graphics code, or when converting user input strings to numbers with Integer.parseInt and then doing math.
flowchart LR
subgraph W["Widening (automatic)"]
I["int"] --> D["double"]
end
subgraph N["Narrowing (explicit cast)"]
D2["double"] -->|"(int)"| I2["int<br/>truncates"]
end
style W fill:#166534,color:#fff
style N fill:#b45309,color:#fff
public class Main {
public static void main(String[] args) {
int a = 7;
double b = 2.5;
System.out.println(a + b);
System.out.println((int) b);
System.out.println((int) (a + 0.9));
}
}
What does this program print? Read it carefully and type the exact output (three lines).
public class Main {
public static void main(String[] args) {
int a = 17;
int b = 5;
System.out.println(a / b);
System.out.println(a % b);
System.out.println(a > b);
}
}
Integer division drops the remainder: 17 / 5 is 3.
% gives the remainder: 17 % 5 is 2.
compares the two values and returns a boolean.
What does this program print? Read it carefully and type the exact output (four lines).
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 3;
boolean c = false;
System.out.println(a + b * 2);
System.out.println((a + b) * 2);
System.out.println(a % b == 1 && !c);
System.out.println(a / b > 2 || c);
}
}
Multiplication happens before addition unless parentheses change the order.
% gives the remainder of integer division: 10 % 3 is 1.
! flips a boolean; && and || combine booleans with short-circuiting.
This code looks like it calculates an average correctly, but it doesn't. Predict the exact two-line output before you check.
public class Main {
public static void main(String[] args) {
int score1 = 83;
int score2 = 92;
double average = (score1 + score2) / 2;
System.out.println(average);
double realAverage = (score1 + score2) / 2.0;
System.out.println(realAverage);
}
}
(score1 + score2) is 175, an int.
175 / 2 uses integer division, giving 87, which becomes 87.0 when stored in double.
175 / 2.0 promotes the numerator to double, giving 87.5.
What is the value of 5 + 2 * 3 > 10 && 4 == 4?
Multiplication first: 2 * 3 = 6. Then 5 + 6 = 11, and 11 > 10 is true. 4 == 4 is also true. true && true evaluates to true.
Mixed types and casting. Predict the exact three-line output.
public class Main {
public static void main(String[] args) {
int a = 7;
int b = 2;
double c = a / b;
double d = (double) a / b;
int e = (int) (a + 0.9);
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
}
a / b is integer division: 7 / 2 = 3, stored as 3.0 in c.
(double) a / b promotes a to double before division: 7.0 / 2 = 3.5.
(int) (7 + 0.9) casts 7.9 to int, truncating to 7.
Recap
- Arithmetic:
+,-,*,/,%. Integer/drops the remainder; use2.0to force decimal division. - Relational:
==,!=,<,>,<=,>=. They returnboolean; do not confuse==with=. - Logical:
&&,||,!.&&and||short-circuit, which makes them safe for guarded checks. - Precedence:
*before+, comparisons before&&,&&before||. Parentheses always win. - Mixed types:
intpromotes todoubleautomatically; narrowing requires an explicit cast like(int).
Next you will see how Java turns the source you write into bytecode the machine can execute.