Skip to main content
Java · lesson 4 of 66
Open the Java page →
Java beginner Lesson 4 of 66

Operators in Java

Run Java's operators and see what they really do: integer division, overflow, ++, short-circuit logic, bitwise and shift operators, ternary, instanceof.

By SkillByExample · Updated

Operators combine values into new values. Most behave as in school arithmetic — the interesting parts are where Java’s types change the answer. Every example is a single file run with the JDK 21 source launcher (java File.java).

Arithmetic: integer division and remainder

public class Arithmetic {
    public static void main(String[] args) {
        System.out.println(7 + 2 + " " + (7 - 2) + " " + 7 * 2);
        System.out.println("7 / 2   = " + 7 / 2);
        System.out.println("7 / 2.0 = " + 7 / 2.0);
        System.out.println("-7 / 2  = " + -7 / 2);
        System.out.println("7 % 3   = " + 7 % 3);
        System.out.println("-7 % 3  = " + -7 % 3);
        System.out.println("5.5 % 2 = " + 5.5 % 2);
        System.out.println("1 + 2 + \"3\" = " + (1 + 2 + "3"));
    }
}
$ java Arithmetic.java
9 5 14
7 / 2   = 3
7 / 2.0 = 3.5
-7 / 2  = -3
7 % 3   = 1
-7 % 3  = -1
5.5 % 2 = 1.5
1 + 2 + "3" = 33

Two ints give an int, so 7 / 2 truncates to 3, and toward zero for negatives. % takes the sign of the left operand, so -7 % 3 is -1 — use Math.floorMod when you need a non-negative result. + works left to right: 1 + 2 is added before meeting the string, and 7 + 2 on the first line became "9" the same way.

Overflow wraps silently

public class Overflow {
    public static void main(String[] args) {
        int big = Integer.MAX_VALUE;
        System.out.println(big);
        System.out.println(big + 1);
        long ok = big + 1L;
        System.out.println(ok);
        System.out.println(1 / 0.0 + " " + (0.0 / 0.0));
        System.out.println(Math.addExact(big, 1));
    }
}
$ java Overflow.java
2147483647
-2147483648
2147483648
Infinity NaN
Exception in thread "main" java.lang.ArithmeticException: integer overflow
	at java.base/java.lang.Math.addExact(Math.java:911)
	at Overflow.main(Overflow.java:9)

int arithmetic wraps around from the largest value to the smallest without any error. Making one operand a long widens the whole expression. Floating point never throws on division by zero; it yields Infinity or NaN. Math.addExact throws instead of wrapping when correctness matters.

Increment, decrement and compound assignment

public class Increment {
    public static void main(String[] args) {
        int a = 5;
        int b = a++;          // use, then increment
        int c = ++a;          // increment, then use
        System.out.println("a=" + a + " b=" + b + " c=" + c);

        int total = 10;
        total += 5;
        total *= 2;
        total -= 3;
        total /= 4;
        System.out.println("total=" + total);

        byte small = 120;
        small += 10;          // hidden cast back to byte
        System.out.println("small=" + small);
    }
}
$ java Increment.java
a=7 b=5 c=7
total=6
small=-126

a++ evaluates to the old value and ++a to the new one; on a line by themselves they are identical. Compound assignment casts its result back to the variable’s type, so small += 10 compiles and overflows to -126 — whereas small = small + 10 would not compile, because small + 10 is an int.

Comparing values and objects

public class Equality {
    public static void main(String[] args) {
        System.out.println((3 < 5) + " " + (3 >= 5) + " " + (3 != 5));

        String a = "hello";
        String b = "hello";
        String c = new String("hello");
        System.out.println("a == b      " + (a == b));
        System.out.println("a == c      " + (a == c));
        System.out.println("a.equals(c) " + a.equals(c));

        Integer x = 127, y = 127, p = 128, q = 128;
        System.out.println("127 == 127  " + (x == y));
        System.out.println("128 == 128  " + (p == q));
        System.out.println("0.1 + 0.2 == 0.3 " + (0.1 + 0.2 == 0.3));
    }
}
$ java Equality.java
true false true
a == b      true
a == c      false
a.equals(c) true
127 == 127  true
128 == 128  false
0.1 + 0.2 == 0.3 false

On objects, == compares references. String literals are interned, so a and b share one object while new String makes another. Boxed Integers from -128 to 127 are cached, which makes == appear to work until 128. And 0.1 + 0.2 is not exactly 0.3 in binary floating point — compare doubles with a tolerance.

Logical operators short-circuit

&& and || skip the right-hand side when the left already decides the answer.

public class ShortCircuit {
    static boolean check(String label, boolean result) {
        System.out.println("  evaluated " + label);
        return result;
    }

    public static void main(String[] args) {
        System.out.println("false && ...");
        boolean r1 = check("left", false) && check("right", true);
        System.out.println("true || ...");
        boolean r2 = check("left", true) || check("right", false);
        System.out.println("false & ... (no short-circuit)");
        boolean r3 = check("left", false) & check("right", true);
        System.out.println(r1 + " " + r2 + " " + r3 + " " + !r1);

        String s = null;
        System.out.println(s != null && s.isEmpty());
    }
}
$ java ShortCircuit.java
false && ...
  evaluated left
true || ...
  evaluated left
false & ... (no short-circuit)
  evaluated left
  evaluated right
false true false true
false

The last line is the everyday use: s.isEmpty() would throw on null, but && never reaches it. & and | evaluate both sides, which is rarely what you want with booleans.

Bitwise operators

&, |, ^ and ~ work on each bit of an integer.

public class Bits {
    static String bin(int n) {
        return String.format("%8s", Integer.toBinaryString(n & 0xFF)).replace(' ', '0');
    }

    public static void main(String[] args) {
        int a = 0b1100, b = 0b1010;
        System.out.println("a     " + bin(a));
        System.out.println("b     " + bin(b));
        System.out.println("a & b " + bin(a & b) + " = " + (a & b));
        System.out.println("a | b " + bin(a | b) + " = " + (a | b));
        System.out.println("a ^ b " + bin(a ^ b) + " = " + (a ^ b));
        System.out.println("~a    = " + ~a);

        final int READ = 1, WRITE = 2, EXEC = 4;
        int perms = READ | WRITE;
        System.out.println("can write? " + ((perms & WRITE) != 0) + ", can exec? " + ((perms & EXEC) != 0));
        perms &= ~WRITE;
        System.out.println("after revoking write: " + perms);
    }
}
$ java Bits.java
a     00001100
b     00001010
a & b 00001000 = 8
a | b 00001110 = 14
a ^ b 00000110 = 6
~a    = -13
can write? true, can exec? false
after revoking write: 1

& keeps bits set in both, | bits set in either, ^ bits set in exactly one. ~a flips every bit of the 32-bit int, which in two’s complement equals -a - 1. Packing flags into one int is the classic use: | sets a flag, & FLAG tests it, &= ~FLAG clears it.

Shift operators

public class Shifts {
    public static void main(String[] args) {
        System.out.println("1 << 4    = " + (1 << 4));
        System.out.println("80 >> 2   = " + (80 >> 2));
        System.out.println("-16 >> 2  = " + (-16 >> 2));
        System.out.println("-16 >>> 2 = " + (-16 >>> 2));
        System.out.println("1 << 32   = " + (1 << 32));
        System.out.println("1L << 32  = " + (1L << 32));
    }
}
$ java Shifts.java
1 << 4    = 16
80 >> 2   = 20
-16 >> 2  = -4
-16 >>> 2 = 1073741820
1 << 32   = 1
1L << 32  = 4294967296

<< n multiplies by 2ⁿ and >> n divides by it, keeping the sign. >>> shifts in zeros, so a negative number becomes a large positive one. An int shift distance is taken modulo 32, which is why 1 << 32 is 1; shift a long to use all 64 bits.

The ternary operator

condition ? a : b is an expression, so it can sit inside an assignment or a call.

public class Ternary {
    public static void main(String[] args) {
        for (int count : new int[] {0, 1, 5}) {
            String label = count == 1 ? "item" : "items";
            System.out.println(count + " " + label);
        }
        int score = 72;
        String grade = score >= 90 ? "A" : score >= 70 ? "B" : "C";
        System.out.println("grade " + grade);
        System.out.println(true ? 1 : 2.0);
    }
}
$ java Ternary.java
0 items
1 item
5 items
grade B
1.0

Chained ternaries read top to bottom like an if/else if ladder, but more than two levels hurts readability. The last line is a trap: when the branches are int and double, the result type is double, so 1 prints as 1.0.

instanceof and pattern matching

public class InstanceOf {
    static String describe(Object o) {
        if (o instanceof String s && !s.isEmpty()) {
            return "String of length " + s.length();
        } else if (o instanceof Integer n) {
            return "Integer, doubled " + n * 2;
        }
        return "something else: " + o;
    }

    public static void main(String[] args) {
        System.out.println(describe("hello"));
        System.out.println(describe(21));
        System.out.println(describe(""));
        System.out.println(describe(null));
        System.out.println(null instanceof Object);
    }
}
$ java InstanceOf.java
String of length 5
Integer, doubled 42
something else: 
something else: null
false

o instanceof String s tests and casts in one step; s can even be used later in the same && condition. instanceof is always false for null, so it doubles as a null check.

Precedence

When an expression mixes operators, precedence decides grouping. Parentheses override it.

public class Precedence {
    public static void main(String[] args) {
        System.out.println(2 + 3 * 4);
        System.out.println((2 + 3) * 4);
        System.out.println(10 - 4 - 3);          // left to right
        System.out.println(2 + 3 > 4 && 1 < 0);  // arithmetic, then comparison, then &&
        boolean t = true, f = false;
        System.out.println(t || f && f);         // && binds tighter than ||
    }
}
$ java Precedence.java
14
20
3
false
true

Multiplication binds tighter than addition, comparisons tighter than &&, and && tighter than ||, so the last line is t || (f && f). Equal-precedence operators group left to right.

Bitwise operators sit below ==, which catches out everyone testing a flag:

public class BadPrecedence {
    public static void main(String[] args) {
        System.out.println(8 & 4 == 4);
    }
}
$ java BadPrecedence.java
BadPrecedence.java:3: error: bad operand types for binary operator '&'
        System.out.println(8 & 4 == 4);
                             ^
  first type:  int
  second type: boolean
1 error
error: compilation failed

It parses as 8 & (4 == 4) — an int AND a boolean. When in doubt, add parentheses.

Practice

  1. Fix BadPrecedence with parentheses so it prints whether bit 4 is set in 8.
Solution
public class BitTest {
    public static void main(String[] args) {
        System.out.println((8 & 4) == 4);
        System.out.println((12 & 4) == 4);
    }
}
$ java BitTest.java
false
true

8 is 1000 in binary and has no 0100 bit; 12 is 1100 and does.

  1. Without using %, test whether a number is even with a bitwise operator. Print the results for -3 to 3.
Solution
public class EvenBits {
    public static void main(String[] args) {
        for (int n = -3; n <= 3; n++) {
            System.out.println(n + " even? " + ((n & 1) == 0));
        }
    }
}
$ java EvenBits.java
-3 even? false
-2 even? true
-1 even? false
0 even? true
1 even? false
2 even? true
3 even? false

The lowest bit is 1 exactly for odd numbers, including negative ones in two’s complement — unlike n % 2 == 1, which is false for -3 because the remainder is -1.

  1. Compute the average of the ints 7 and 8 so it prints 7.5, not 7.
Solution
public class Average {
    public static void main(String[] args) {
        int a = 7, b = 8;
        System.out.println((a + b) / 2);
        System.out.println((a + b) / 2.0);
        System.out.println((double) (a + b) / 2);
    }
}
$ java Average.java
7
7.5
7.5

The cast must apply before the division; (double) ((a + b) / 2) would convert the already truncated 7.

  1. Use Math.floorMod to wrap a day-of-week index: starting from day 1, go back 10 days in a 7-day week. Compare it with %.
Solution
public class Wrap {
    public static void main(String[] args) {
        int day = 1, offset = -10;
        System.out.println("%        -> " + (day + offset) % 7);
        System.out.println("floorMod -> " + Math.floorMod(day + offset, 7));
    }
}
$ java Wrap.java
%        -> -2
floorMod -> 5

% keeps the dividend’s sign and gives -2, which is not a valid index; floorMod always returns a value from 0 to 6.

Frequently Asked Questions

What is the difference between == and .equals() in Java?
For primitives, == compares values. For objects, == asks whether two references point to the same object, while equals compares contents as the class defines them. Two String objects with the same text can be unequal under ==, so always compare strings with equals.
Why does 7 / 2 give 3 in Java?
When both operands are integers, / performs integer division and discards the fractional part, truncating toward zero. Make at least one operand a floating-point value, as in 7 / 2.0 or (double) 7 / 2, to get 3.5.
What does the >>> operator do?
>>> is the unsigned right shift. It always fills the vacated high bits with zeros, while >> copies the sign bit, so for a negative int >> keeps the result negative and >>> produces a large positive number. For non-negative values the two give the same result.
What is the difference between & and && in Java?
Both compute a logical AND on booleans, but && short-circuits: if the left side is false, the right side is never evaluated. & always evaluates both sides, and on integers it is the bitwise AND. Use && for conditions such as s != null && s.isEmpty().
What does pattern matching instanceof do?
Since Java 16, obj instanceof String s both tests the type and, if it matches, declares s as the object already cast to String. The variable is in scope only where the test is known to be true, which removes the separate cast older code needed.