Operators
Operators are the symbols that tell Java how to manipulate variables and values. From adding two numbers to flipping bits in memory, operators are the building blocks of every expression you write.
Categories of Operators
Java groups its operators into several categories. The table below gives you a quick orientation before we dive into each one.
| Category | Symbols | Purpose |
|---|---|---|
| Arithmetic | + - * / % | Math computations |
| Unary | + - ++ -- ! | Single-operand operations |
| Relational | == != < > <= >= | Compare two values |
| Logical | && || ! | Combine boolean conditions |
| Bitwise | & | ^ ~ << >> >>> | Bit-level manipulation |
| Assignment | = += -= *= … | Assign (and optionally compute) |
| Ternary | ? : | Inline if-else expression |
instanceof | instanceof | Type check at runtime |
Arithmetic Operators
These perform the everyday math operations you already know.
public class Arithmetic {
public static void main(String[] args) {
int a = 17, b = 5;
System.out.println(a + b); // 22 — addition
System.out.println(a - b); // 12 — subtraction
System.out.println(a * b); // 85 — multiplication
System.out.println(a / b); // 3 — integer division (truncates)
System.out.println(a % b); // 2 — modulus (remainder)
}
}
Output:
22
12
85
3
2
Warning: Dividing two
intvalues performs integer division — it drops the decimal part. If you need the fractional result, cast at least one operand todouble:(double) a / b→3.4.
The % (modulus) operator is extremely useful for tasks like checking if a number is even (n % 2 == 0) or cycling through array indices.
Unary Operators
Unary operators work on a single operand.
public class UnaryDemo {
public static void main(String[] args) {
int x = 10;
System.out.println(+x); // 10 — unary plus (rarely needed)
System.out.println(-x); // -10 — negation
System.out.println(!true); // false — logical NOT
// Pre-increment: increment THEN use
System.out.println(++x); // 11
// Post-increment: use THEN increment
System.out.println(x++); // 11 (x becomes 12 after this line)
System.out.println(x); // 12
}
}
Output:
10
-10
false
11
11
12
Pre vs Post Increment/Decrement
This trips up nearly every beginner at some point. The rule is simple:
- Pre (
++x/--x): change the value first, then evaluate the expression. - Post (
x++/x--): evaluate the expression first, then change the value.
int n = 5;
int a = ++n; // n becomes 6, a = 6
int b = n++; // b = 6, then n becomes 7
System.out.println(a + " " + b + " " + n); // 6 6 7
Output:
6 6 7
Relational (Comparison) Operators
Relational operators compare two values and always produce a boolean result (true or false). They are the backbone of if-else and loop conditions.
int x = 10, y = 20;
System.out.println(x == y); // false — equal to
System.out.println(x != y); // true — not equal to
System.out.println(x < y); // true — less than
System.out.println(x > y); // false — greater than
System.out.println(x <= 10); // true — less than or equal
System.out.println(y >= 20); // true — greater than or equal
Output:
false
true
true
false
true
true
Warning: Never use
==to compareStringobjects — it checks reference equality (whether two variables point to the same object), not content equality. Use.equals()instead. See String Comparison for the full explanation.
Logical Operators
Logical operators combine boolean expressions. Java provides two styles: short-circuit (&&, ||) and non-short-circuit (&, |).
int age = 25;
boolean hasId = true;
// && — true only if BOTH sides are true
System.out.println(age >= 18 && hasId); // true
// || — true if AT LEAST ONE side is true
System.out.println(age < 18 || hasId); // true
// ! — flips a boolean
System.out.println(!hasId); // false
Output:
true
true
false
Short-Circuit Evaluation
&& and || are short-circuit operators — they stop evaluating as soon as the result is determined.
int[] arr = null;
// Without short-circuit, arr.length would throw NullPointerException
// With &&, if arr == null is true, the right side is never evaluated
if (arr != null && arr.length > 0) {
System.out.println("Array has elements");
} else {
System.out.println("Array is null or empty");
}
Output:
Array is null or empty
This pattern — checking for null before accessing a member — is extremely common and relies entirely on short-circuit behavior.
Assignment Operators
The basic assignment operator = stores a value into a variable. Compound assignment operators combine a computation with assignment, making code more concise.
int n = 10;
n += 5; // same as: n = n + 5 → 15
n -= 3; // same as: n = n - 3 → 12
n *= 2; // same as: n = n * 2 → 24
n /= 4; // same as: n = n / 4 → 6
n %= 4; // same as: n = n % 4 → 2
System.out.println(n); // 2
Output:
2
Compound operators also work for bitwise operations: &=, |=, ^=, <<=, >>=.
Ternary Operator
The ternary operator ? : is a compact, one-line alternative to if-else. It takes three operands: a condition, a value when true, and a value when false.
int score = 72;
String grade = (score >= 60) ? "Pass" : "Fail";
System.out.println(grade); // Pass
Output:
Pass
Tip: Use the ternary operator for simple, readable assignments. Avoid nesting ternaries inside each other — that quickly becomes hard to read. Reach for a full
if-elseblock instead.
Bitwise Operators
Bitwise operators work directly on the binary (bit) representation of integer values. They are fast and are commonly used in performance-sensitive code, flag manipulation, and low-level programming.
| Operator | Name | Effect |
|---|---|---|
& | AND | 1 if both bits are 1 |
| | OR | 1 if either bit is 1 |
^ | XOR | 1 if bits differ |
~ | NOT | Flips all bits |
<< | Left shift | Multiply by 2^n |
>> | Signed right shift | Divide by 2^n (keeps sign) |
>>> | Unsigned right shift | Divide by 2^n (fills 0s) |
int a = 6; // binary: 0110
int b = 3; // binary: 0011
System.out.println(a & b); // 2 (0010)
System.out.println(a | b); // 7 (0111)
System.out.println(a ^ b); // 5 (0101)
System.out.println(~a); // -7 (flips all 32 bits)
System.out.println(a << 1); // 12 (1100) — shift left by 1 = ×2
System.out.println(a >> 1); // 3 (0011) — shift right by 1 = ÷2
Output:
2
7
5
-7
12
3
A very common idiom: use << and >> as fast multiplications and divisions by powers of two, and use & with a mask to test or clear specific bits.
int flags = 0b1010; // bits 1 and 3 set
boolean bit1Set = (flags & 0b0010) != 0; // test bit 1
System.out.println(bit1Set); // true
The instanceof Operator
instanceof tests whether an object is an instance of a given class or interface — without throwing a ClassCastException. It always returns a boolean.
Object obj = "Hello";
System.out.println(obj instanceof String); // true
System.out.println(obj instanceof Integer); // false
Output:
true
false
Since Java 16, the pattern matching form of instanceof (finalized from preview in Java 14) lets you test and cast in one step:
Object obj = "Hello, World!";
if (obj instanceof String s) {
// s is already a String here — no explicit cast needed
System.out.println(s.toUpperCase());
}
Output:
HELLO, WORLD!
See Pattern Matching for the full picture, and instanceof Operator for more examples.
Operator Precedence
When an expression contains multiple operators, Java evaluates them in a fixed order called precedence. Higher precedence operators bind tighter (evaluate first).
| Precedence (high → low) | Operators |
|---|---|
| Postfix | expr++ expr-- |
| Unary | ++expr --expr + - ~ ! |
| Multiplicative | * / % |
| Additive | + - |
| Shift | << >> >>> |
| Relational | < > <= >= instanceof |
| Equality | == != |
| Bitwise AND | & |
| Bitwise XOR | ^ |
| Bitwise OR | | |
| Logical AND | && |
| Logical OR | || |
| Ternary | ? : |
| Assignment | = += -= … |
int result = 2 + 3 * 4; // 14, not 20 — * before +
int result2 = (2 + 3) * 4; // 20 — parentheses override
boolean b = 5 > 3 && 10 < 20; // true — > and < before &&
Tip: When in doubt, use parentheses. They cost nothing at runtime and make your intent crystal clear to the next reader (including your future self).
Under the Hood
Integer Arithmetic and Overflow
Java’s int is a 32-bit signed integer. If a computation exceeds Integer.MAX_VALUE (2,147,483,647), it silently wraps around — it does not throw an exception.
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648 — wraps to MIN_VALUE
Output:
-2147483648
If overflow is a concern, use long (64-bit), or the Math.addExact() / Math.multiplyExact() methods (Java 8+) that throw ArithmeticException on overflow.
How the JVM Handles Operators
At the bytecode level, Java compiles most operators to dedicated JVM instructions:
+onint→iadd-onint→isub*onint→imul/onint→idiv<<→ishl>>→ishr>>>→iushr
The JIT compiler (see JIT Compilation) further lowers these to native CPU instructions. Shift and bitwise operations are single CPU cycles, making them the fastest operations available. Addition and multiplication are nearly as fast on modern hardware. Division (/ and %) is typically 20–40× slower than addition because integer division requires a separate CPU circuit.
Promotion Rules
When you mix types in an arithmetic expression, Java promotes the smaller type:
byte/short/charoperands are always promoted tointbefore the operation.- If one operand is
long,float, ordouble, the other is promoted to match.
byte x = 10;
byte y = 20;
// byte z = x + y; // COMPILE ERROR — result is int, not byte
int z = x + y; // correct
This is why you sometimes need an explicit cast when assigning back to a narrower type. See Data Types for the full promotion chain.
Related Topics
- Variables — the containers operators act upon
- Data Types — understand how type promotion affects operator results
- Control Statements — use relational and logical operators to build conditions
- if-else Statement — put relational operators to work in branching logic
- JIT Compilation — how the JVM optimizes operator bytecode to native code
- Pattern Matching — modern use of
instanceofwith pattern variables (Java 16+)