Module 2 · Core Syntax & Types ⏱ 19 min

Operators & Expressions

By the end of this lesson you will be able to:
  • Evaluate arithmetic, relational, and logical expressions in Java
  • Apply operator precedence rules to predict the result of mixed expressions
  • Recognize the short-circuit behavior of && and ||
  • Identify integer division and mixed-type arithmetic traps

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
Operator precedence in Java, from highest to lowest. Parentheses always win.

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.

Main.java — integer division versus decimal division. Real, compiling Java.
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
Short-circuit evaluation guards the right side when the left side is already decisive.
Main.java — relational and logical operators with short-circuiting. Real, compiling Java.
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
Widening happens automatically; narrowing requires an explicit cast and truncates.
Main.java — mixed types, promotion, and casting. Real, compiling Java.
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));
    }
}
Exercise

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);
    }
}
Exercise

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);
    }
}
Exercise

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);
    }
}
Exercise

What is the value of 5 + 2 * 3 > 10 && 4 == 4?

Exercise

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);
    }
}

Recap

  • Arithmetic: +, -, *, /, %. Integer / drops the remainder; use 2.0 to force decimal division.
  • Relational: ==, !=, <, >, <=, >=. They return boolean; 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: int promotes to double automatically; narrowing requires an explicit cast like (int).

Next you will see how Java turns the source you write into bytecode the machine can execute.

Checkpoint quiz

What does 10 / 3 evaluate to in Java when both operands are ints?

Given boolean x = false;, what is the result of true || x && false?

Go deeper — technical resources