BeginnerReading time: ~17 min

Operators

Arithmetic, logical, bitwise, comparison operators and short-circuit evaluation

Java operators and expressions: arithmetic, relational, logical, bitwise, assignment, ternary, instanceof operators, precedence, and evaluation order.


1. Definition

What is it?

An operator is a symbol that performs an operation on one or more operands and produces a result (value). An expression is a combination of operators and operands that evaluates to a single value.

Why does it matter?

  • Fundamental building block: every algorithm, condition, and loop relies on operators
  • Performance: choosing the right operator (e.g., bitwise shift vs. multiplication) can make a meaningful difference
  • Readability: understanding precedence and short-circuit evaluation is essential for bug-free code

Where does it fit?

Operators belong to the syntactic foundation layer of Java — directly above types and variables, and below control structures.


2. Core Concepts

2.1 Arithmetic Operators

Operator Description Example Result
+ Addition 5 + 3 8
- Subtraction 5 - 3 2
* Multiplication 5 * 3 15
/ Division 7 / 2 3 (integer division!)
% Modulo (remainder) 7 % 2 1
++ Increment i++ / ++i pre/post increment
-- Decrement i-- / --i pre/post decrement

⚠ Integer division: when both operands are integers, the result is an integer (the decimal part is truncated).

2.2 Relational / Comparison Operators

Operator Description Example
== Equal to a == b
!= Not equal to a != b
> Greater than a > b
< Less than a < b
>= Greater than or equal a >= b
<= Less than or equal a <= b

🔑 For primitives, == compares values. For objects, == compares references.

2.3 Logical Operators

Operator Description Short-circuit?
&& Logical AND ✅ Yes — if left is false, right is not evaluated
|| Logical OR ✅ Yes — if left is true, right is not evaluated
! Logical NOT —
& Logical AND (eager) ❌ No
| Logical OR (eager) ❌ No

2.4 Bitwise Operators

Operator Description Example
& Bitwise AND 0b1010 & 0b1100 → 0b1000
| Bitwise OR 0b1010 | 0b1100 → 0b1110
^ Bitwise XOR 0b1010 ^ 0b1100 → 0b0110
~ Bitwise NOT (complement) ~0b1010 → 0b...0101
<< Left shift 1 << 3 → 8
>> Signed right shift -8 >> 2 → -2
>>> Unsigned right shift -1 >>> 28 → 15

2.5 Assignment Operators

Operator Equivalent
= Simple assignment
+= a = a + b
-= a = a - b
*= a = a * b
/= a = a / b
%= a = a % b
&= a = a & b
|= a = a | b
^= a = a ^ b
<<= a = a << b
>>= a = a >> b
>>>= a = a >>> b

🔑 Compound assignment operators (e.g., +=) include an implicit cast: short s = 1; s += 1; ✅ compiles, but s = s + 1; ❌ compile error (int → short narrowing).

2.6 Ternary Operator (Conditional Expression)

result = (condition) ? valueIfTrue : valueIfFalse;

This is the only three-operand (ternary) operator in Java.

2.7 instanceof Operator

if (obj instanceof String s) {   // pattern matching (Java 16+)
    System.out.println(s.length());
}
  • null instanceof Anything → always false
  • Since Java 16, pattern matching allows declaring a variable inside instanceof

2.8 Operator Precedence Table

Priority Operator(s) Associativity
1 (highest) () [] . Left to right
2 ++ -- (postfix) Left to right
3 ++ -- (prefix) + - (unary) ~ ! Right to left
4 (type) cast Right to left
5 * / % Left to right
6 + - Left to right
7 << >> >>> Left to right
8 < <= > >= instanceof Left to right
9 == != Left to right
10 & Left to right
11 ^ Left to right
12 | Left to right
13 && Left to right
14 || Left to right
15 ? : Right to left
16 (lowest) = += -= etc. Right to left

2.9 Evaluation Order vs. Precedence

These two concepts are often confused.

  • Precedence decides how an expression is grouped
  • Evaluation order decides when operands are evaluated

Java guarantees left-to-right operand evaluation, but that does not remove the need for parentheses. A readable expression is usually better than a clever one.


3. Practical Usage

When to Use Which Operator?

Task Recommended Why?
Even/odd check n % 2 == 0 Simple, readable
Multiply/divide by power of 2 n << 1 / n >> 1 Performance (though JIT optimizes anyway)
Null-safe check obj != null && obj.method() Short-circuit guards against NullPointerException
Simple if-else value Ternary ? : More concise, but don't nest!
Flag management Bitwise &, |, ^ Memory-efficient, fast
Type checking instanceof + pattern matching Modern Java 16+ idiom

Practical Rules of Thumb

  • Prefer clarity over brevity in conditionals
  • Use parentheses even when precedence would technically be correct
  • Avoid side effects inside expressions that are also doing branching
  • Use bitwise operators only when the domain is genuinely bit-oriented
  • Prefer Objects.equals(a, b) when null-safety matters

4. Code Examples

4.1 Basic Examples

// Integer division — common surprise
int a = 7 / 2;        // 3, NOT 3.5!
double b = 7.0 / 2;   // 3.5 — at least one operand is double

// Pre vs. post increment
int i = 5;
int x = i++;  // x = 5, i = 6 (post: assign first, then increment)
int y = ++i;  // y = 7, i = 7 (pre: increment first, then assign)

// Modulo with negative numbers
int mod = -7 % 3;  // -1 (result sign follows the dividend's sign)

4.2 Short-Circuit Evaluation

// ✅ Correct: short-circuit guards against NullPointerException
String name = null;
if (name != null && name.length() > 0) {
    System.out.println(name);
}

// ❌ Wrong: & is not short-circuit, will throw NullPointerException!
if (name != null & name.length() > 0) {  // đŸ’„ NPE!
    System.out.println(name);
}

4.3 Bitwise Flag Management

// Define flags
static final int READ    = 0b0001;  // 1
static final int WRITE   = 0b0010;  // 2
static final int EXECUTE = 0b0100;  // 4

// Set a flag
int permissions = READ | WRITE;          // 0b0011 = 3

// Check a flag
boolean canRead = (permissions & READ) != 0;   // true

// Clear a flag
permissions = permissions & ~WRITE;             // 0b0001 = 1

// Toggle a flag
permissions = permissions ^ EXECUTE;            // 0b0101 = 5

4.4 Ternary — Good and Bad Usage

// ✅ Simple and readable
String status = (age >= 18) ? "adult" : "minor";

// ❌ Nested ternary — unreadable!
String category = (age < 13) ? "child" : (age < 18) ? "teenager" : "adult";

// ✅ Use if-else or switch expression instead
String category;
if (age < 13) category = "child";
else if (age < 18) category = "teenager";
else category = "adult";

4.5 Compound Assignment Implicit Cast

short s = 10;
// s = s + 1;   // ❌ Compile error! (s + 1 = int, int → short narrowing)
s += 1;         // ✅ OK! += includes implicit cast: s = (short)(s + 1)

byte b = 127;
b += 1;         // ✅ Compiles, BUT overflow! b = -128

5. Trade-offs

Aspect Option A Option B When to choose?
Readability vs. brevity if-else Ternary ? : Ternary for simple one-liner choices; if-else for complex logic
Bitwise vs. conventional n * 2 n << 1 On modern JVMs there's no real speed difference; n * 2 is more readable
Short-circuit vs. eager && / || & / | Almost always short-circuit; eager only when both side effects are needed
== vs. equals() == .equals() == for primitives and reference identity; .equals() for content comparison

6. Common Mistakes

❌ 6.1 Integer Division Surprise

double result = 1 / 3;          // 0.0, not 0.333!
double correct = 1.0 / 3;       // 0.333...  ✅
double alsoCorrect = (double) 1 / 3;  // 0.333...  ✅

❌ 6.2 Precedence Pitfall

// Intent: (a & b) != 0
if (a & b != 0) { }   // ❌ Actually: a & (b != 0) — != has higher precedence than &!
if ((a & b) != 0) { }  // ✅ Use parentheses!

❌ 6.3 == vs. equals on Objects

String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2;        // false — different references!
s1.equals(s2);   // true  ✅

❌ 6.4 Pre/Post Increment Side Effects

int i = 0;
int result = i++ + ++i;  // ❌ Confusing, looks platform-specific
// Well-defined in Java (result = 0 + 2 = 2), but AVOID this pattern!

❌ 6.5 Overflow in Compound Assignment

byte b = 127;
b += 1;     // -128! No compile-time warning, silent overflow

7. Deep Dive

7.1 Absence of Operator Overloading

Unlike C++ and Kotlin, Java does not support operator overloading. The only exception is the + operator for String concatenation, which the compiler rewrites to StringBuilder or invokedynamic (Java 9+).

7.2 Expression Evaluation Order (JLS §15.7)

The Java Language Specification guarantees that operands are evaluated left-to-right. This is not the same as precedence:

int i = 0;
int r = (i = 1) + (i = 2);  // r = 1 + 2 = 3, NOT 2 + 2!
// The left operand is evaluated before the right operand

7.3 Numeric Promotion Rules

  • If either operand is double → result is double
  • Otherwise, if either is float → result is float
  • Otherwise, if either is long → result is long
  • Otherwise: both are promoted to int (even if byte + byte!)
byte a = 10, b = 20;
// byte c = a + b;  // ❌ Compile error! a + b = int
int c = a + b;      // ✅

7.4 NaN and Infinity Behavior

double nan = Double.NaN;
nan == nan;           // false! NaN is never equal to itself
Double.isNaN(nan);    // true  ✅

double inf = 1.0 / 0; // Infinity, not ArithmeticException!
int zero = 1 / 0;     // đŸ’„ ArithmeticException (integer division)

7.5 Switch Expression vs. Ternary (Java 14+)

// Java 14+ switch expression — more readable than nested ternary
String type = switch (code) {
    case 1 -> "admin";
    case 2 -> "user";
    default -> "guest";
};

7.6 Bit Masks Still Matter in Real Systems

Even though many applications can use enums and booleans, bit masks still appear in permission systems, protocol parsers, binary file formats, and low-level frameworks.

The important rule is not “never use bitwise operators”, but “use them where the data model is truly bit-based and document the intent clearly”.

7.7 Side Effects in Expressions Hurt Maintainability

This is legal Java:

if (cache != null && cache.refreshIfExpired()) {
    // ...
}

But mixing state mutation and condition checking makes debugging harder. In business code, splitting the logic into separate steps is often the better trade-off.


8. Interview Questions

What is the difference between precedence and evaluation order?

Precedence determines how the compiler groups operators, while evaluation order determines when operands are executed. In Java, operands are evaluated left-to-right, but precedence still decides the structure of the expression.

Why is `&&` usually preferred over `&` in conditions?

Because && short-circuits. If the left side is already false, Java skips the right side, which both improves readability and prevents accidental NullPointerException in guard conditions.

Why does `byte + byte` become `int`?

Because Java applies numeric promotion rules during arithmetic. Operands smaller than int are promoted to int before the operation, so the result must be stored in int unless you cast explicitly.

When is the ternary operator a good choice?

It is a good choice when both branches are short, side-effect free, and clearly express a value. Once nesting starts or the branches do meaningful work, if-else or switch is usually better.


9. Glossary

Term Definition
Operator A symbol that performs an operation on operand(s)
Operand The value on which an operator acts
Expression A combination of operators and operands that evaluates to a single value
Precedence The priority order in which operators are evaluated
Associativity When precedence is equal, the direction of evaluation (left-to-right or right-to-left)
Short-circuit evaluation Skipping evaluation of the right operand when the result is already determined
Bitwise An operation applied to individual bits
Ternary operator A three-operand conditional expression (? :)
Numeric promotion Automatic conversion of smaller types to larger types during operations
Compound assignment Combined operation and assignment (+=, -=, etc.) with implicit cast
instanceof Type-checking operator, with pattern matching support since Java 16
Unary operator An operator with a single operand (e.g., !, ~, ++)
Binary operator An operator with two operands (e.g., +, &&)

10. Cheatsheet

  • Integer division: 7 / 2 = 3 — use 7.0 / 2 or explicit cast for decimals
  • Short-circuit: && and || → right side not evaluated if result is already determined
  • Precedence: when in doubt, use parentheses — (a & b) != 0
  • == vs. equals: == for primitives, .equals() for objects
  • Compound assignment: += includes implicit cast → s += 1 compiles, s = s + 1 doesn't (if s is short/byte)
  • Numeric promotion: byte + byte = int → explicit cast needed to assign back
  • NaN: Double.NaN != Double.NaN → use Double.isNaN()
  • instanceof null: null instanceof Anything → always false
  • Bitwise shift: 1 << n = 2ⁿ — useful for flags and powers of two
  • Ternary: one level deep max — if more complex, use if-else or switch expression
  • Evaluation order: JLS guarantees left-to-right evaluation, but don't rely on it with side effects!
  • Operator overloading: Java has none — except + for String concatenation

🎼 Games

10 questions