Operators

Java's operators — arithmetic, relational, logical, bitwise, assignment, and more — are the building blocks every expression is assembled from.

An operator performs an operation on one or more operands, producing a value. Java groups operators into several families by what they do: arithmetic, unary, assignment, relational, equality, logical, bitwise, shift, conditional, and increment/decrement. Understanding what each family does — and how operand type affects behavior — is foundational to reading and writing any Java expression.

Arithmetic Operators

The five arithmetic operators work on numeric operands (and, for +, also on Strings, where it means concatenation).

Integer division truncates toward zero, discarding any fractional part; % on integers returns the remainder with the sign of the dividend. Floating-point division and remainder follow IEEE 754 rules instead — e.g. 5.0 / 0.0 yields Infinity, not an exception.

int a = 17, b = 5;
System.out.println(a + b); // 22
System.out.println(a - b); // 12
System.out.println(a * b); // 85
System.out.println(a / b); // 3  (integer division truncates)
System.out.println(a % b); // 2  (remainder)

Unary Operators

Unary operators act on a single operand: + (unary plus, rarely used, has no real effect), - (negation), ! (logical NOT, boolean operands only), and ~ (bitwise complement, integer operands only).

int x = 5;
System.out.println(-x);   // -5
System.out.println(~x);   // -6 (bitwise complement: flips every bit)
boolean flag = true;
System.out.println(!flag); // false

Assignment and Compound Assignment

= assigns a value to a variable. The compound assignment operators (+=, -=, *=, /=, %=, and the bitwise/shift compound forms) combine an operation with assignment, and are not merely shorthand — they also implicitly cast the result back to the target variable's type.

int x = 10;
x += 5;    // x = x + 5 -> 15

byte b = 10;
// b = b + 5;   // COMPILE ERROR: b + 5 promotes to int, doesn't fit back into byte implicitly
b += 5;         // OK: compound assignment implicitly narrows the result back to byte

Relational and Equality Operators

Relational operators (<, >, <=, >=) compare ordering, and work only on numeric (and char) operands. Equality operators (==, !=) test for equality, but mean different things depending on operand type: for primitives, value equality; for reference types, identity (same object), not content.

int a = 5, b = 10;
System.out.println(a < b);   // true
System.out.println(a == b);  // false

String s1 = new String("hi");
String s2 = new String("hi");
System.out.println(s1 == s2);      // false — different objects
System.out.println(s1.equals(s2)); // true — same content

Logical Operators

&&(AND), ||(OR), and !(NOT) operate on boolean operands and produce boolean results. && and || are short-circuiting: the right operand is not evaluated at all if the left operand already determines the result, which matters both for performance and for avoiding side effects/exceptions.

Java also has non-short-circuiting logical operators & and | when applied to boolean operands — both sides are always evaluated. These are the same symbols used for bitwise operations on integer operands; context (operand type) determines the meaning.

String text = null;
if (text != null && text.length() > 0) {  // safe: short-circuit skips length() when text is null
    System.out.println("non-empty");
}

Bitwise Operators

&, |, and ^ operate bit-by-bit on integer operand types (byte, short, char, int, long), performing bitwise AND, OR, and XOR respectively.

int a = 0b1100; // 12
int b = 0b1010; // 10
System.out.println(a & b); // 0b1000 = 8
System.out.println(a | b); // 0b1110 = 14
System.out.println(a ^ b); // 0b0110 = 6

Shift Operators

<< shifts bits left (filling with zeros), >> shifts bits right performing sign extension (arithmetic shift — preserves the sign for negative numbers), and >>> shifts bits right filling with zeros regardless of sign (logical/unsigned shift).

int x = -8;
System.out.println(x >> 1);  // -4  (sign-preserving)
System.out.println(x >>> 1); // large positive number (zero-filled, ignores sign)
System.out.println(4 << 2);  // 16  (4 * 2^2)

Conditional (Ternary) Operator

?: is Java's only three-operand operator: condition ? valueIfTrue : valueIfFalse. It is an expression, producing a value, unlike an if/else statement.

int a = 7, b = 3;
int max = (a > b) ? a : b;   // 7

Increment and Decrement

++ and -- increase or decrease a numeric variable by 1. Each comes in a prefix form (++x) that evaluates to the new value, and a postfix form (x++) that evaluates to the original value before the change, even though both forms perform the same underlying increment.

int x = 5;
int a = ++x;  // x becomes 6 first, then a = 6
int y = 5;
int b = y++;  // b = 5 (original value), then y becomes 6

instanceof Operator

instanceof tests whether an object is an instance of a given type (or a subtype of it), producing a boolean. It never throws for a null operand — null instanceof AnyType is always false.

Object obj = "hello";
System.out.println(obj instanceof String);  // true
System.out.println(null instanceof String); // false, no exception

Operator Behavior by Operand Type

The same operator symbol can mean genuinely different things depending on operand type — this is worth internalizing rather than memorizing case by case.

+   on numbers   -> addition
+   on Strings   -> concatenation
&   on booleans  -> non-short-circuit logical AND
&   on integers  -> bitwise AND
==  on primitives -> value equality
==  on references -> identity (same object?) equality
%   on ints       -> integer remainder
%   on doubles     -> floating-point remainder (IEEE 754 rules)
System.out.println(1 + 2 + "3"); // "33" — left-to-right: 1+2=3 (int), then 3+"3"="33" (concatenation)
System.out.println("1" + 2 + 3); // "123" — left-to-right: "1"+2="12", then "12"+3="123"

Complete Example

public class OperatorsDemo {
    public static void main(String[] args) {
        int a = 13, b = 4;
        System.out.println(a / b + " r " + a % b); // 3 r 1

        boolean isAdult = true, hasId = false;
        System.out.println(isAdult && hasId); // false, short-circuits after checking hasId

        int score = 85;
        String grade = (score >= 90) ? "A" : (score >= 80) ? "B" : "C";
        System.out.println(grade); // B

        int flags = 0b0101;
        flags |= 0b0010; // set a bit
        System.out.println(Integer.toBinaryString(flags)); // 111

        int count = 0;
        System.out.println(count++); // 0
        System.out.println(count);   // 1
    }
}

Quick Reference

Arithmetic:    +  -  *  /  %
Unary:         +  -  !  ~
Assignment:    =  +=  -=  *=  /=  %=  &=  |=  ^=  <<=  >>=  >>>=
Relational:    <  >  <=  >=
Equality:      ==  !=
Logical:       &&  ||  !     (short-circuit)
Bitwise:       &  |  ^  ~    (also & | on booleans, non-short-circuit)
Shift:         <<  >>  >>>
Conditional:   ?:
Increment/dec: ++  --  (prefix and postfix)
instanceof:    type test, false for null, never throws

Key Takeaways

Java groups operators into families: arithmetic, unary, assignment, relational, equality, logical, bitwise, shift, conditional, and increment/decrement.

The same symbol can behave differently depending on operand type — + concatenates Strings but adds numbers; & is bitwise on integers but non-short-circuit logical AND on booleans.

&& and || short-circuit; & and | (on booleans) always evaluate both operands.

== means identity for reference types and value equality for primitives — never content equality for objects.

Compound assignment operators implicitly cast their result back to the target variable's type.

instanceof is null-safe and never throws.

if (name == "Ada") {... } if (name.equals("Ada")) {... } == on String references compares identity, not text content. Use.equals() to compare String content.

if (flag = true) {... } // assignment, not comparison if (flag == true) {... } // or simply: if (flag)

= is assignment, == is comparison. Assigning inside a condition compiles (because it evaluates to the assigned boolean value) but almost never expresses intent.

byte b = 10; b = b + 5; // does not compile byte b = 10; b += 5; // compiles: compound assignment casts implicitly b + 5 promotes to int under numeric promotion rules and cannot be assigned back to byte without an explicit cast — but the compound assignment operator performs that narrowing cast implicitly.

if (str != null & str.length() > 0) {... } if (str != null && str.length() > 0) {... }

The single & always evaluates both operands. If str is null, str.length() still runs and throws NullPointerException. && short-circuits and skips the right operand once the left is already false.

Why does 1 + 2 + "3" evaluate to the String "33" while "1" + 2 + 3 evaluates to "123"?

Operators of equal precedence evaluate left to right. In the first case, 1 + 2 is numeric addition (yielding 3) before it ever meets a String, so it becomes 3 + "3" = "33". In the second case, the leftmost operand is already a String, so "1" + 2 immediately becomes concatenation ("12"), and "12" + 3 continues concatenating to "123".

What is the difference between && and &, when both operands are boolean expressions?

&& short-circuits: if the left operand is false, the right operand is never evaluated. & always evaluates both operands regardless of the left operand's value.

Why is null instanceof String legal and safe, evaluating to false rather than throwing?

instanceof is specifically defined to return false when the tested reference is null, rather than attempting to dereference it — no NullPointerException occurs.

What is the difference between x++ and ++x as expressions (not as statements)?

Both increment x by 1, but ++x (prefix) evaluates to the new, incremented value, while x++ (postfix) evaluates to the original value x held before the increment.

Why does == behave differently for primitive types versus reference types?

For primitives, == compares the actual stored values directly. For reference types, a variable's 'value' is the reference (handle) itself, so == compares whether two references point to the exact same object — it never inspects or compares object content.

What is the practical danger of using & instead of && when the right operand could throw an exception if evaluated unnecessarily?

& always evaluates both operands, so even when the left operand already makes the overall result determinate (e.g. false for AND), the right operand still runs — if it depends on a condition the left operand was guarding against (like a null check), it can throw an exception that && would have avoided by short-circuiting.

Why does 7 >> 1 differ from 7 >>> 1 for a negative number but not for a positive one like 7?

>> is an arithmetic (sign-preserving) right shift, and >>> is a logical (zero-fill) right shift. For a positive number the sign bit is already 0, so both shifts fill with 0 and produce the same result; for a negative number the sign bit is 1, so >> fills with 1s (preserving the negative sign) while >>> fills with 0s (producing a large positive result), giving different outcomes.

Write an expression using the ternary operator that assigns "even" or "odd" to a String variable based on whether an int n is even.

String parity = (n % 2 == 0) ? "even" : "odd";

The modulus operator % gives the remainder of n divided by 2; comparing it to 0 with == determines evenness, and the ternary operator selects the appropriate String without needing an if/else statement.

What does this code print for x and y?

x prints 7, y prints 11. x++ evaluates to 5 (original value) then x becomes 6; ++x then increments x to 7 and evaluates to 7; y = 5 + 7 = 12... actually tracing carefully: x++ yields 5, x is now 6; ++x increments x to 7 and yields 7; y = 5 + 7 = 12, and final x = 7. (Correction applied: y is 12, x is 7.)

Write code demonstrating why & should not be used in place of && when checking for null before calling a method on a possibly-null reference.

String text = null; // Unsafe: always evaluates text.length(), throws NullPointerException // if (text != null & text.length() > 0) { } // Safe: short-circuits, never calls text.length() when text is null if (text != null && text.length() > 0) { System.out.println("non-empty"); } else { System.out.println("null or empty"); }

Using && ensures text.length() is only evaluated once text != null has already been confirmed true, since && short-circuits on a false left operand; & would evaluate both sides unconditionally and throw NullPointerException when text is null.

int x = 5;
int y = x++ + ++x;
System.out.println(x);
System.out.println(y);