5. Java Operators
Table of Contents
- 5.1 Definition
- 5.2 Types of Operators
- 5.3 Categories of Operators
- 5.4 Operator Precedence and Order of Evaluation
- 5.5 Summary Table of Java Operators
- 5.6 Unary Operators
- 5.7 Binary Operators
- 5.8 Ternary Operator
5.1 Definition
In Java, operators are special symbols that perform operations on variables and values.
They are the building blocks of expressions and allow developers to manipulate data, compare values, perform arithmetic, and control logic flow.
An expression is a combination of operators and operands that produces a result.
For example:
int result = (a + b) * c;
Here, + and * are operators, and a, b, and c are operands.
5.2 Types of Operators
Java defines three types of operators, grouped by the number of operands they use:
| Type | Description | Examples |
|---|---|---|
| Unary | Operate on a single operand | +x, -x, ++x, --x, !flag, ~num |
| Binary | Operate on two operands | a + b, a - b, x * y, x / y, x % y |
| Ternary | Operate on three operands (only one in Java) | condition ? valueIfTrue : valueIfFalse |
5.3 Categories of Operators
Operators can also be grouped, by their purpose, into categories:
| Category | Description | Examples |
|---|---|---|
| Assignment | Assign values to variables | =, +=, -=, *=, /=, %= |
| Relational | Compare values | ==, !=, <, >, <=, >= |
| Logical | Combine or invert boolean expressions | |, &, ^ |
| Conditional | Combine or invert boolean expressions | ||, && |
| Bitwise | Manipulate bits | &, |, ^, ~, <<, >>, >>> |
| Instanceof | Test object type | obj instanceof ClassName |
| Lambda | Used in lambda expressions | (x, y) -> x + y |
5.4 Operator Precedence and Order of Evaluation
Operator precedence determines how operators are grouped in an expression — that is, which operations are performed first.
Associativity (or order of evaluation) determines whether the expression is evaluated from left to right or right to left when operators have the same precedence.
Example:
int result = 10 + 5 * 2; // Multiplication happens before addition → result = 20
Parentheses () can be used to override precedence:
int result = (10 + 5) * 2; // Parentheses evaluated first → result = 30
Note
- Operator precedence is about grouping, not evaluation order.
- Use parentheses for precedence and clarity in complex expressions.
5.5 Summary Table of Java Operators
| Precedence (High → Low) | Type | Operators | Example | Evaluation Order | Applicable To |
|---|---|---|---|---|---|
| 1 | Postfix Unary | expr++, expr-- |
x++ |
Left → Right | Numeric types |
| 2 | Prefix Unary | ++expr, --expr |
--x |
Left → Right | Numeric |
| 3 | Other Unary | (type), +, -, ~, ! |
-x, !flag |
Right → Left | Numeric, boolean |
| 4 | Cast Unary | (Type) reference |
(short) 22 |
Right → Left | reference, primitive |
| 5 | Multiplication/division/modulus | *, /, % |
a * b |
Left → Right | Numeric types |
| 6 | Additive | +, - |
a + b |
Left → Right | Numeric, String (concatenation) |
| 7 | Shift | <<, >>, >>> |
a << 2 |
Left → Right | Integral types |
| 8 | Relational | <, >, <=, >=, instanceof |
a < b, obj instanceof Person |
Left → Right | Numeric, reference |
| 9 | Equality | ==, != |
a == b |
Left → Right | All types (except boolean for <, >) |
| 10 | Logical AND | & |
a & b |
Left → Right | boolean |
| 11 | Logical exclusive OR | ^ |
a ^ b |
Left → Right | boolean |
| 12 | Logical inclusive OR | | |
a|b |
Left → Right | boolean |
| 13 | Conditional AND | && |
a&&b |
Left → Right | boolean |
| 14 | Conditional OR | || |
a||b |
Left → Right | boolean |
| 15 | Ternary (Conditional) | ? : |
a > b ? x : y |
Right → Left | All types |
| 16 | Assignment | =, +=, -=, *=, /=, %= |
x += 5 |
Right → Left | All assignable types |
| 17 | Arrow operator | -> |
(x, y) -> x + y |
Right → Left | Lambda expressions, switch rules |
5.5.1 Additional Notes
- String concatenation (
+) has lower precedence than arithmetic+with numbers. - Use parentheses
()for precedence and readability — they don’t change semantics but make intent explicit.
5.6 Unary Operators
Unary operators operate on a single operand to produce a new value.
They are used for operations like incrementing/decrementing, negating a value, inverting a boolean, or performing bitwise complement.
5.6.1 Categories of Unary Operators
| Operator | Name | Description | Example | Result |
|---|---|---|---|---|
+ |
Unary plus | Indicates a positive value (usually redundant). | +x |
Same as x |
- |
Unary minus | Indicates a literal number is negative or negates an expression. | -5 |
-5 |
++ |
Increment | Increases a variable by 1. Can be prefix or postfix. | ++x, x++ |
x+1 |
-- |
Decrement | Decreases a variable by 1. Can be prefix or postfix. | --x, x-- |
x-1 |
! |
Logical complement | Inverts a boolean value. | !true |
false |
~ |
Bitwise complement | Inverts each bit of an integer. Quick rule: ~n = -(n + 1) | ~5 |
-6 |
(type) |
Cast | Converts value to another type. | (int) 3.9 |
3 |
5.6.2 Examples
int x = 5;
System.out.println(++x); // 6 (prefix: increments x to 6, then returns 6)
System.out.println(x++); // 6 (postfix: returns 6, then increments x to 7)
System.out.println(x); // 7
boolean flag = false;
System.out.println(!flag); // true
int a = 5; // binary: 0000 0101
System.out.println(~a); // -6 → binary: 1111 1010 (two's complement)
Note
- Prefix (
++x/--x): updates the value first, then returns the new value. - Postfix (
x++/x--): returns the current value first, then updates it. - The
!operator applies to boolean values;~applies to integral numeric types.
5.7 Binary Operators
Binary operators require two operands.
They perform arithmetic, relational, logical, bitwise, and assignment operations.
5.7.1 Categories of Binary Operators
| Category | Operators | Example | Description |
|---|---|---|---|
| Arithmetic | +, -, *, /, % |
a + b |
Basic math operations. |
| Relational | <, >, <=, >=, ==, != |
a < b |
Compare values. |
| Logical (boolean) | &, |, ^ |
a&b |
See note below |
| Conditional | &&, || |
a&&b |
See note below |
| Bitwise (integral) | &, |, ^, <<, >>, >>> |
a << 2 |
Operate on bits. |
| Assignment | =, +=, -=, *=, /=, %= |
x += 3 |
Modify and assign. |
| String Concatenation | + |
"Hello " + name |
Joins strings together. |
Important
- Logical operators (
&,|,^) always evaluate both sides. - Conditional operators (
&&,||) are short-circuiting:a && b→bevaluated only ifais truea || b→bevaluated only ifais false
Important
Cheat Sheet Pattern Bitwise and Boolean
a ^ a = 0
a ^ 0 = a
a ^ -1 = ~a
a ^ ~a = -1
a & a = a
a | 0 = a
Arithmetic Example:
int a = 10, b = 4;
System.out.println(a + b); // 14
System.out.println(a - b); // 6
System.out.println(a * b); // 40
System.out.println(a / b); // 2
System.out.println(a % b); // 2
Relational Example:
int a = 5, b = 8;
System.out.println(a < b); // true
System.out.println(a >= b); // false
System.out.println(a == b); // false
System.out.println(a != b); // true
Logical Example:
boolean x = true, y = false;
System.out.println(x && y); // false
System.out.println(x || y); // true
System.out.println(!x); // false
Bitwise Example:
int a = 5; // 0101
int b = 3; // 0011
System.out.println(a & b); // 1 (0001)
System.out.println(a | b); // 7 (0111)
System.out.println(a ^ b); // 6 (0110)
System.out.println(a << 1); // 10 (1010)
System.out.println(a >> 1); // 2 (0010)
5.7.2 Division and Modulus Operators
5.7.2.1 Division Operator
Dividing an integer by zero (for example, 10 / 0) causes the JVM to throw a java.lang.ArithmeticException: / by zero.
However, floating-point division behaves differently.
When a float or double value is divided by 0 or 0.0, no exception is thrown. Instead, the result is:
- Float.POSITIVE_INFINITY or Float.NEGATIVE_INFINITY
- Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY
The sign depends on the operands involved in the operation.
To determine whether a floating-point value represents infinity, the Float and Double classes provide utility methods:
Static methods:
- Float.isInfinite(float value)
- Double.isInfinite(double value)
Instance methods:
- Float.isInfinite()
- Double.isInfinite()
These methods return true if the value corresponds to either positive or negative infinity.
5.7.2.2 Modulus Operator
The modulus operator is the remainder when two numbers are divided. If two numbers divide evenly, the reminder is 0: for example 10 % 5 is 0. On the other hand, 13 % 4 gives the reminder of 1.
We can use modulus with negative numbers according to the following rules:
- if the divisor is negative (Ex: 7 % -5), then the sign is ignored and the result is 2;
- if the dividend is negative (Ex: -7 % 5), then the sign is preserved and the result is -2;
System.out.println(8 % 5); // GIVES 3
System.out.println(10 % 5); // GIVES 0
System.out.println(10 % 3); // GIVES 1
System.out.println(-10 % 3); // GIVES -1
System.out.println(10 % -3); // GIVES 1
System.out.println(-10 % -3); // GIVES -1
System.out.println(8 % 9); // GIVES 8
System.out.println(3 % 4); // GIVES 3
System.out.println(2 % 4); // GIVES 2
System.out.println(-8 % 9); // GIVES -8
5.7.3 The Return Value of an Assignment Operator
In Java, the assignment operator (=) not only stores a value in a variable —
it also returns the assigned value as the result of the entire expression.
This means that the assignment operation itself can be used as part of another expression,
such as inside an if statement, a loop condition, or even another assignment.
int x;
int y = (x = 10); // the assignment (x = 10) returns 10
System.out.println(y); // 10
// x = 10 assigns 10 to x.
// The expression (x = 10) evaluates to 10.
// That value is then assigned to y.
// So both x and y end up with the same value (10).
Because assignment returns a value, it can also appear inside an if statement. However, this often leads to logical errors if used unintentionally.
boolean flag = false;
if (flag = true) {
System.out.println("This will always execute!");
}
// Here the condition (flag = true) assigns true to flag, and then evaluates to true, so the if block always runs.
// Correct usage (comparison instead of assignment):
if (flag == true) {
System.out.println("Condition checked, not assigned");
}
Warning
If you see if (x = something), stop: it’s assignment, not comparison.
5.7.4 Compound Assignment Operators
Compound assignment operators in Java combine an arithmetic or bitwise operation with assignment in a single step.
Instead of writing x = x + 5, you can use the shorthand x += 5.
They automatically perform type casting to the left-hand variable type when necessary.
Common compound operators include:
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, and >>>=.
int x = 10;
// Arithmetic compound assignments
x += 5; // same as x = x + 5 → x = 15
x -= 3; // same as x = x - 3 → x = 12
x *= 2; // same as x = x * 2 → x = 24
x /= 4; // same as x = x / 4 → x = 6
x %= 5; // same as x = x % 5 → x = 1
// Bitwise compound assignments
int y = 6; // 0110 (binary)
y &= 3; // y = y & 3 → 0110 & 0011 = 0010 → y = 2
y |= 4; // y = y | 4 → 0010 | 0100 = 0110 → y = 6
y ^= 5; // y = y ^ 5 → 0110 ^ 0101 = 0011 → y = 3
// Shift compound assignments
int z = 8; // 0000 1000
z <<= 2; // z = z << 2 → 0010 0000 → z = 32
z >>= 1; // z = z >> 1 → 0001 0000 → z = 16
z >>>= 2; // z = z >>> 2 → 0000 0100 → z = 4
// Type casting example
byte b = 10;
// b = b + 1; // ❌ compile-time error: int result cannot be assigned to byte
b += 1; // ✅ works: implicit cast back to byte
Note
Compound assignments perform an implicit cast to the variable type on the left.
That’s why b += 1 compiles even though b = b + 1 does not.
5.7.5 Equality Operators (== and !=)
The equality operators in Java == (equal to) and != (not equal to) are used to compare two operands for equality.
However, their behavior differs depending on whether they are applied to primitive types or reference types (objects).
Note
==compares values for primitives==compares references for objects.equals()compares object content (if implemented)
5.7.5.1 Equality with Primitive Types
When comparing primitive values, == and != compare the actual stored values.
int a = 5, b = 5;
System.out.println(a == b); // true → both have the same value
System.out.println(a != b); // false → values are equal
Important
- If the operands are of different numeric types, Java automatically promotes them to a common type before comparison.
- However, comparing float and double can produce unexpected results due to precision errors (check example below)
int x = 10;
double y = 10.0;
System.out.println(x == y); // true → x promoted to double (10.0)
double d = 0.1 + 0.2;
System.out.println(d == 0.3); // false → floating-point rounding issue
5.7.5.2 Equality with Reference Types (Objects)
For objects, == and != compare references, not object content. They return true only if both references point to the exact same object in memory.
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2); // false → different objects in memory
System.out.println(s1 != s2); // true → not the same reference
Even if two objects have identical content, == compares their addresses, not values. To compare the contents of objects, use the .equals() method instead.
System.out.println(s1.equals(s2)); // true → same string content
Special Case: null and String Literals
- Any reference can be compared with null using == or !=.
String text = null;
System.out.println(text == null); // true
- String literals are interned by the Java Virtual Machine (JVM): This means identical literal strings may point to the same reference in memory:
```java String a = "Java"; String b = "Java"; System.out.println(a == b); // true → same interned literal
- Equality with Mixed Types:
When using == between operands of different categories (e.g., primitive vs. object),
the compiler tries to perform unboxing if one of them is a **wrapper class**.
```java
Integer i = 100;
int j = 100;
System.out.println(i == j); // true → unboxed before comparison
5.7.6 The instanceof Operator
instanceof is a relational operator that tests whether a reference value is an instance of a given reference type at runtime.
It returns a boolean.
Object o = "Java";
boolean b1 = (o instanceof String); // true
boolean b2 = (o instanceof Number); // false
null behavior: If expr is null, expr instanceof Type is always false.
Object n = null;
System.out.println(n instanceof Object); // false
Warning
instanceof always returns false when the left operand is null.
5.7.6.1 Compile-Time Check vs Runtime Check
- At compile time, the compiler rejects inconvertible types (types that cannot possibly relate at runtime).
- At runtime, if the compile-time check passed, the JVM evaluates the actual object type.
// ❌ Compile-time error: inconvertible types (String is unrelated to Integer)
boolean bad = ("abc" instanceof Integer);
// ✅ Compiles, but runtime result depends on actual object:
Number num = Integer.valueOf(10);
System.out.println(num instanceof Integer); // true at runtime
System.out.println(num instanceof Double); // false at runtime
5.7.6.2 Pattern Matching for instanceof
Java supports type patterns with instanceof, which both test and bind the variable when the test succeeds. Adding a variable after the type instructs the compiler to treat it as Pattern Matching
Syntax (pattern form):
Object obj = "Hello";
if (obj instanceof String str) {
// Adding the variable str after the type instructs the compiler to treat it as Pattern Matching
System.out.println(str.toUpperCase()); // identifier is in scope here, of type Type: (safe: str is a String here).
}
Key properties:
- If the test succeeds, the pattern variable (e.g., s) is definitely assigned and in scope in the true branch.
- Pattern variables are implicitly final (cannot be reassigned).
- The name must not clash with an existing variable in the same scope.
5.7.6.3 Flow Scoping & Short-Circuit Logic
Pattern variables become available based on flow analysis:
Object obj = "data";
// Negated test, variable available in the else branch
if (!(obj instanceof String s)) {
// s not in scope here
} else {
System.out.println(s.length()); // s is in scope here
}
// With &&, pattern variable can be used on the right side if the left side established it
if (obj instanceof String s && s.length() > 3) {
System.out.println(s.substring(0, 3)); // s in scope
}
// With ||, the pattern variable is NOT safe on the right side (short-circuit may fail to establish it)
if (obj instanceof String s || s.length() > 3) { // ❌ s not in scope here
// ...
}
// Parentheses can help group logic
if ((obj instanceof String s) && s.contains("a")) { // ✅ s in scope after grouped test
System.out.println(s);
}
Pattern matching with null evaluates, like always for instanceof, to false
String str = null;
// Regular instanceof
if (str instanceof String) {
System.out.print("NOT EXECUTED"); // instanceof evaluates to false
}
// Pattern matching
if (str instanceof String s) {
System.out.print("NOT EXECUTED"); // instanceof still evaluates to false
}
Supported Types:
The type of the pattern variable must be a subtype, a supertype or of the same type of the reference variable.
Number num = Short.valueOf(10);
if (num instanceof String s) {} // ❌ Compile-time error
if (num instanceof Short s) {} // ✅ Ok
if (num instanceof Object s) {} // ✅ Ok
if (num instanceof Number s) {} // ✅ Ok
5.7.6.4 Arrays and Reifiable Types
instanceof works with arrays (which are reifiable) and with erased or wildcard generic forms.
Reifiable types are those whose runtime representation fully preserves their type (e.g., raw types, arrays, non-generic classes, wildcard ?).
Due to type erasure, List
Object arr = new int[]{1,2,3};
System.out.println(arr instanceof int[]); // true
Object list = java.util.List.of(1,2,3);
// System.out.println(list instanceof List<Integer>); // ❌ Compile-time error: parameterized type not reifiable
System.out.println(list instanceof java.util.List<?>); // ✅ true
5.8 Ternary Operator
The ternary operator (? :) is the only operator in Java that takes three operands.
It acts as a concise form of an if-else statement.
5.8.1 Type Rules for the Ternary Operator
The type of a conditional (ternary) expression is determined by the types of its second and third operands.
5.8.1.1 Numeric Operands
- If one operand is
byteand the other isshort, the result type isshort. - If one operand is of type
T(byte,short, orchar) and the other operand is anintconstant expression whose value fits within typeT, then the result type isT. - In all other numeric cases, binary numeric promotion is applied to the second and third operands.
The type of the conditional expression becomes the promoted type.
Binary numeric promotion includes unboxing conversion and value set conversion.
5.8.1.2 Reference Types
- If one operand is
nulland the other is a reference type, the result type is that reference type. - If the operands are different reference types, one type must be assignment-compatible with the other.
The resulting type is the more general type (the one to which the other can be assigned). - If neither type can be assigned to the other, the expression causes a compile-time error.
In short, the ternary operator determines its type by applying:
- Special narrowing rules for small integral types
- Binary numeric promotion for numeric values
- Assignment compatibility rules for reference types
Tip
The ternary operator must produce a value of a compatible type. If the two branches produce unrelated types, compilation fails.
String s = true ? "ok" : 5; // ❌ compile error: incompatible types
5.8.2 Syntax
condition ? expressionIfTrue : expressionIfFalse;
5.8.3 Example
int age = 20;
String access = (age >= 18) ? "Allowed" : "Denied";
System.out.println(access); // "Allowed"
5.8.4 Nested Ternary Example
int score = 85;
String grade = (score >= 90) ? "A" :
(score >= 75) ? "B" :
(score >= 60) ? "C" : "F";
System.out.println(grade); // "B"
5.8.5 Notes
Warning
- Nested ternary expressions can reduce readability. Use parentheses for clarity.
- The ternary operator returns a value, unlike
if-else,