Java Operators
Operators are symbols that perform operations on values. Java groups them by what they do: arithmetic, assignment, comparison, logical, bitwise, and a few special cases. Each group has rules about operand types, result types, and evaluation order that determine what an expression actually computes.
Arithmetic Operators
| Operator | Operation | Example | Result |
|---|---|---|---|
+ | Addition | 7 + 2 | 9 |
- | Subtraction | 7 - 2 | 5 |
* | Multiplication | 7 * 2 | 14 |
/ | Division | 7 / 2 | 3 |
% | Remainder (modulus) | 7 % 2 | 1 |
Integer division
When both operands are integers, / discards the fractional part:
System.out.println(7 / 2); // 3
System.out.println(-7 / 2); // -3 (truncates toward zero)
System.out.println(7 / 2.0); // 3.5 (one operand is double)
System.out.println(7 / 0); // ArithmeticException: / by zero
System.out.println(7 / 0.0); // Infinity (floating-point division by zero does not throw)
Remainder
% returns what is left after integer division. Its sign follows the left operand:
System.out.println(7 % 3); // 1
System.out.println(-7 % 3); // -1
System.out.println(7 % -3); // 1
System.out.println(7.5 % 2); // 1.5 (works on doubles too)
Common uses: n % 2 == 0 tests for even numbers; i % size wraps an index around a circular buffer; seconds % 60 extracts the seconds part of a duration. For a result that is always non-negative, use Math.floorMod(-7, 3), which gives 2.
Unary plus and minus
int a = 5;
int b = -a; // -5
int c = +a; // 5 (no effect; exists for symmetry)
Increment and Decrement
++ adds one; -- subtracts one. Each has a prefix and a postfix form that differ in what value the expression yields.
int i = 5;
int a = i++; // a is 5, then i becomes 6 (postfix: use, then increment)
int b = ++i; // i becomes 7, then b is 7 (prefix: increment, then use)
As a standalone statement (i++;) the two are identical. The difference matters only when the result is used in a larger expression, and code that depends on it (arr[i++] = x;) is harder to read than two separate statements.
Assignment Operators
= stores a value. The compound forms combine an operation with assignment:
| Operator | Equivalent |
|---|---|
x += y | x = x + y |
x -= y | x = x - y |
x *= y | x = x * y |
x /= y | x = x / y |
x %= y | x = x % y |
x &= y, x |= y, x ^= y | Bitwise and/or/xor with assignment |
x <<= y, x >>= y, x >>>= y | Shift with assignment |
Compound assignment includes an implicit cast back to the variable's type, so byte b = 10; b += 5; compiles while b = b + 5; does not.
Assignment is itself an expression whose value is the assigned value, so it chains:
int x, y, z;
x = y = z = 0; // evaluated right to left
Writing = where == was intended is a compile error in a condition because an int is not a boolean. The exception is boolean variables: if (flag = true) compiles and always assigns true. Write if (flag) instead.
Comparison Operators
All comparison operators produce a boolean.
| Operator | Meaning |
|---|---|
== | Equal to |
!= | Not equal to |
> | Greater than |
< | Less than |
>= | Greater than or equal to |
<= | Less than or equal to |
int age = 20;
boolean adult = age >= 18; // true
boolean teen = age >= 13 && age <= 19; // false
<, >, <=, >= work only on numeric types (including char). == and != work on everything, with one critical distinction:
- On primitives,
==compares values. - On references,
==compares identity: whether both variables point at the same object.
String s1 = new String("hi");
String s2 = new String("hi");
System.out.println(s1 == s2); // false: two different objects
System.out.println(s1.equals(s2)); // true: same content
Use equals to compare the content of objects (String, Integer, LocalDate, your own classes). Use == for primitives, for null checks (x == null), and for enum constants, which are guaranteed unique.
Logical Operators
Logical operators combine boolean values.
| Operator | Meaning | true when |
|---|---|---|
&& | AND | Both operands are true |
|| | OR | At least one operand is true |
! | NOT | The operand is false |
boolean loggedIn = true;
boolean isAdmin = false;
System.out.println(loggedIn && isAdmin); // false
System.out.println(loggedIn || isAdmin); // true
System.out.println(!isAdmin); // true
Short-circuit evaluation
&& and || evaluate the right operand only if needed. If the left side of && is false, the result is false regardless, so the right side is skipped. If the left side of || is true, the right side is skipped.
String name = null;
if (name != null && name.length() > 0) { // safe: length() is never called on null
System.out.println(name);
}
Without short-circuiting, name.length() would throw NullPointerException. Ordering conditions so that the cheap or guarding check comes first is a standard pattern.
The single-character forms & and | also work on booleans but evaluate both sides always. They are rarely wanted for logic; their main use is bitwise arithmetic on integers.
Ternary (Conditional) Operator
condition ? valueIfTrue : valueIfFalse is the only operator with three operands. It is an expression, so it produces a value.
int score = 72;
String result = score >= 50 ? "pass" : "fail";
int max = a > b ? a : b;
Both branches must produce compatible types. It replaces a four-line if/else that merely assigns a variable. Nesting ternaries (a ? b : c ? d : e) is legal but hard to read; use if/else or a switch expression for more than two outcomes.
Bitwise and Shift Operators
These operate on the individual bits of integer types. They appear in low-level code: flags, masks, compression, hashing, networking, graphics.
| Operator | Name | 5 op 3 (binary 101, 011) |
|---|---|---|
& | AND | 001 = 1 |
| | OR | 111 = 7 |
^ | XOR | 110 = 6 |
~ | NOT (complement) | ~5 = -6 |
<< | Left shift | 5 << 1 = 10 |
>> | Signed right shift | -8 >> 1 = -4 |
>>> | Unsigned right shift | -8 >>> 28 = 15 |
int read = 0b100, write = 0b010, exec = 0b001;
int perms = read | write; // 0b110: combine flags
boolean canWrite = (perms & write) != 0; // test a flag
perms &= ~write; // clear a flag: 0b100
Left-shifting by n multiplies by 2ⁿ; right-shifting divides by 2ⁿ. >> preserves the sign bit (arithmetic shift); >>> fills with zeros (logical shift). The shift amount is taken modulo 32 for int and modulo 64 for long, so 1 << 33 on an int is 1 << 1.
String Concatenation
+ with at least one String operand produces a new String:
String s = "Total: " + 5 + 3; // "Total: 53"
String t = "Total: " + (5 + 3); // "Total: 8"
String u = 5 + 3 + " items"; // "8 items"
Evaluation is left to right. In s, "Total: " + 5 is already a string, so the final + 3 appends text. In u, 5 + 3 happens first because both are numbers. Any object concatenated to a string has its toString() called; null becomes the text "null".
instanceof
instanceof tests whether a reference refers to an object of a given type (or subtype, or implementing class):
Object o = "text";
if (o instanceof String s) { // pattern matching form, Java 16+
System.out.println(s.length());
}
null instanceof Anything is always false.
Operator Precedence
When an expression contains several operators, precedence determines which is applied first. Higher rows bind tighter.
| Precedence | Operators | Associativity |
|---|---|---|
| 1 (highest) | x++ x-- (postfix) | Left |
| 2 | ++x --x +x -x ! ~ (unary) | Right |
| 3 | * / % | Left |
| 4 | + - | Left |
| 5 | << >> >>> | Left |
| 6 | < > <= >= instanceof | Left |
| 7 | == != | Left |
| 8 | & | Left |
| 9 | ^ | Left |
| 10 | | | Left |
| 11 | && | Left |
| 12 | || | Left |
| 13 | ? : | Right |
| 14 (lowest) | = += -= etc. | Right |
int r = 2 + 3 * 4; // 14, not 20: * before +
boolean b = 5 > 3 && 2 > 1; // comparisons before &&
int m = 10 - 4 - 3; // 3: left associative, (10 - 4) - 3
Two practical rules follow from the table:
- Arithmetic binds tighter than comparison, which binds tighter than logical operators.
a + b > c && dis((a + b) > c) && dwith no parentheses needed. - Bitwise
&and|bind looser than==.flags & MASK == 0is parsed asflags & (MASK == 0), which is a compile error. Always write(flags & MASK) == 0.
When precedence is not obvious to a reader, add parentheses. They cost nothing at runtime and prevent misreading.
Evaluation Order
Separately from precedence, Java guarantees that operands are evaluated left to right, and that an operator's operands are fully evaluated before the operator is applied.
int i = 1;
int result = i++ + i++; // 1 + 2 = 3, and i is 3 afterward
The left i++ yields 1 and sets i to 2; the right i++ yields 2 and sets i to 3. This is well-defined in Java (unlike C, where it is undefined behavior), but code that relies on it is confusing and should be split into separate statements.
Method arguments are also evaluated left to right, so f(a(), b()) always calls a before b.
Practical Example
public class PriceCalculator {
public static void main(String[] args) {
double unitPrice = 24.99;
int quantity = 4;
boolean isMember = true;
int couponPercent = 15;
double subtotal = unitPrice * quantity;
double discount = isMember ? subtotal * couponPercent / 100.0 : 0;
double total = subtotal - discount;
boolean freeShipping = total >= 75 || (isMember && quantity > 2);
int whole = (int) total;
int cents = (int) Math.round((total - whole) * 100);
System.out.printf("Subtotal: %.2f%n", subtotal);
System.out.printf("Discount: %.2f%n", discount);
System.out.printf("Total: %d.%02d%n", whole, cents);
System.out.println("Free shipping: " + freeShipping);
}
}
Output:
Subtotal: 99.96
Discount: 14.99
Total: 84.97
Free shipping: true
subtotal * couponPercent / 100.0 is evaluated left to right: the multiplication happens first, then the division by a double, so no integer truncation occurs. The ternary gives a discount of zero to non-members. || short-circuits: once total >= 75 is true, the member check is skipped. %02d zero-pads the cents to two digits so 5 prints as 05.