3.1 Arithmetic, Relational, and Logical Operators

Key Takeaways

  • Binary arithmetic operators (+, -, *, /, %) perform binary numeric promotion, widening operands narrower than int to int, or promoting to the widest operand type (long, float, double).
  • Integer division truncates towards zero by dropping any fractional decimal remainder; dividing or taking modulus by integer zero throws an unchecked ArithmeticException at runtime.
  • Floating-point division and modulus operations conform to IEEE 754 standards, returning Infinity, -Infinity, or NaN without throwing an ArithmeticException.
  • The modulus operator (%) calculates remainder using the formal formula a - (a / b) * b, meaning the resulting sign strictly matches the dividend (left operand) while the divisor's sign is ignored.
  • Short-circuit logical operators (&&, ||) skip evaluation of the right operand when the left operand determines the overall truth value, preventing runtime errors such as NullPointerException and division by zero.
Last updated: September 2026

3.1 Arithmetic, Relational, and Logical Operators

[!NOTE] Exam Focus: Two of Oracle's 13 published topic areas land here — "Working with Java Operator" (six objectives, the largest single topic on the outline) and part of "Using Decision Statements". Oracle publishes no percentage weightings for 1Z0-811, but by raw objective count this is the densest region of the syllabus. Candidates are rigorously tested on binary numeric promotion, integer division truncation versus floating-point division producing special IEEE 754 values, the modulus sign rule with negative operands, primitive equality versus object reference comparison, and short-circuit logical evaluation side effects.

In Java, operators are special symbols or keywords that direct the Java Virtual Machine (JVM) to perform specific mathematical, relational, or logical calculations on one, two, or three data values. The values or expressions operated upon are termed operands. Java strictly classifies operators based on the number of operands they require:

  1. Unary Operators: Operate on a single operand (e.g., ++count, -delta, !isValid, ~bitmask).
  2. Binary Operators: Operate on two operands positioned on either side in infix notation (e.g., a + b, x * y, p && q).
  3. Ternary Operator: Operates on three operands, providing an inline conditional expression (condition ? exprTrue : exprFalse).

Every operator expression produces a concrete resulting value possessing an exact data type dictated by the operator and the types of its evaluated operands.


1. Binary Arithmetic Operators & Binary Numeric Promotion

Java provides five fundamental binary arithmetic operators for numeric computation:

OperatorMathematical OperationExample ExpressionEvaluated Result
+Addition (or String Concatenation)14 + 519
-Subtraction14 - 59
*Multiplication14 * 570
/Division (Integer or Floating-Point)14 / 52 (integer) or 2.8 (double)
%Modulus (Remainder after division)14 % 54

Binary Numeric Promotion Rules (JLS §5.6.2)

Whenever a binary arithmetic operator is applied to numeric operands, the JVM applies binary numeric promotion prior to calculation. The operands are widened according to these four sequential rules:

  1. If either operand is double, the other operand is widened to double, and the operation yields a double.
  2. Otherwise, if either operand is float, the other operand is widened to float, and the operation yields a float.
  3. Otherwise, if either operand is long, the other operand is widened to long, and the operation yields a long.
  4. Otherwise, both operands are promoted to int, and the operation yields a 32-bit int.
byte b1 = 10;
byte b2 = 20;
// byte b3 = b1 + b2; // COMPILE ERROR: incompatible types: possible lossy conversion from int to byte
int sum = b1 + b2;    // Compiles cleanly: b1 and b2 are promoted to int before addition
byte b3 = (byte)(b1 + b2); // Compiles cleanly with an explicit narrowing cast

[!WARNING] The Narrow Integer Promotion Trap: Arithmetic on byte, short, or char always promotes operands to int. Even if you add two short primitives (short s = 1 + 2;), literal addition is constant-folded, but variable addition (short s3 = s1 + s2;) fails compilation without an explicit cast back to short.


2. Integer Division vs. IEEE 754 Floating-Point Division

The division operator (/) behaves fundamentally differently depending on whether its operands are integer types or floating-point types.

Integer Division: Truncation Toward Zero

When both operands are integral types (byte, short, char, int, long), Java performs integer division. In integer division, the result is strictly an integer. The JVM discards any fractional decimal part without rounding toward the nearest integer—it always truncates toward zero:

int res1 = 15 / 4;   // Evaluates to 3 (15 / 4 = 3.75 -> truncated to 3)
int res2 = 3 / 4;    // Evaluates to 0 (3 / 4 = 0.75 -> truncated to 0)
int res3 = -15 / 4;  // Evaluates to -3 (-3.75 truncated toward zero -> -3)

Integer Division by Zero: ArithmeticException

Attempting to divide an integer by literal 0 or an integer variable holding 0 compiles cleanly (because 0 is a valid integer literal), but the JVM halts execution at runtime and throws an unchecked java.lang.ArithmeticException: / by zero:

int numerator = 100;
int denominator = 0;
int outcome = numerator / denominator; // Throws java.lang.ArithmeticException at runtime

Floating-Point Division: IEEE 754 Special Values

If at least one operand is a floating-point type (float or double), Java executes floating-point division conforming to the IEEE 754 standard. Truncation does not take place, and an ArithmeticException is never thrown:

  1. Positive Non-Zero divided by 0.0: Produces positive infinity (Double.POSITIVE_INFINITY, printed as Infinity).
  2. Negative Non-Zero divided by 0.0: Produces negative infinity (Double.NEGATIVE_INFINITY, printed as -Infinity).
  3. Zero divided by 0.0: Produces Not-a-Number (Double.NaN, printed as NaN).
double d1 = 15.0 / 4;   // 3.75 (4 promoted to 4.0)
double d2 = 15 / 4;     // 3.0 (Integer division 15 / 4 yields 3, which widens to 3.0!)
double d3 = (double)15 / 4; // 3.75 (15 cast to 15.0 prior to division)
double posInf = 25.0 / 0.0; // Infinity (no exception)
double negInf = -25.0 / 0.0; // -Infinity (no exception)
double notANum = 0.0 / 0.0; // NaN (no exception)
ExpressionOperand TypesEvaluated ValueThrows Exception?
9 / 2int / int4No
9 / 2.0int / double4.5No
(double)(9 / 2)(double)(int)4.0No (integer division evaluates first)
(double)9 / 2double / int4.5No (9 cast to 9.0 first)
10 / 0int / intNoneYes: ArithmeticException
10.0 / 0double / intInfinityNo
-10.0 / 0.0double / double-InfinityNo
0.0 / 0.0double / doubleNaNNo

3. The Modulus (Remainder) Operator (%)

The modulus operator (%) calculates the remainder resulting from dividing the left operand (dividend) by the right operand (divisor).

Mathematical Definition in the Java Language Specification (JLS §15.17.3)

In Java, the modulus operator is strictly defined by the formula:

a%b=a(a/b)ba \% b = a - (a / b) * b

Because integer division a / b truncates toward zero, the sign of the remainder is entirely determined by the dividend a.

The Golden Dividend Sign Rule

On the 1Z0-811 examination, questions testing the modulus operator frequently present combinations of positive and negative numbers. Memorize this absolute rule:

The algebraic sign of the result of a modulus operation strictly matches the sign of the left operand (the dividend). The sign of the right operand (the divisor) is completely ignored!

System.out.println( 19 %  5); //  4  (Dividend +19 is positive -> +4)
System.out.println(-19 %  5); // -4  (Dividend -19 is negative -> -4)
System.out.println( 19 % -5); //  4  (Dividend +19 is positive -> +4; divisor sign ignored)
System.out.println(-19 % -5); // -4  (Dividend -19 is negative -> -4; divisor sign ignored)

Let us trace -19 % -5 using the official JLS formula:

  1. -19 / -5 = 3 (negative divided by negative is positive 3; truncation preserves 3).
  2. 3 * (-5) = -15.
  3. -19 - (-15) = -19 + 15 = -4.

Notice that despite both operands being negative, the modulus result is -4, strictly preserving the negative sign of the dividend.

Floating-Point Modulus

Unlike C and C++ where % requires integer operands, Java permits modulus operations on float and double:

double rem = 7.5 % 2.0; // 1.5 (7.5 - (3 * 2.0) = 1.5)
double zeroRem = 5.5 % 0.0; // Double.NaN (no exception!)

Taking integer modulus by zero (10 % 0) throws an ArithmeticException, whereas floating-point modulus by zero (10.0 % 0.0) produces NaN.


4. String Concatenation Operator (+)

The plus symbol (+) is the only operator in Java that is overloaded by the language specification. If either operand in a binary addition expression is a String object, Java treats + as the String Concatenation Operator rather than arithmetic addition.

Left-to-Right Evaluation and Precedence

Because binary + associates from left to right, the order of numeric and string operands dictates the outcome:

System.out.println(1 + 2 + "3"); // Prints "33" (1 + 2 = 3; 3 + "3" = "33")
System.out.println("1" + 2 + 3); // Prints "123" ("1" + 2 = "12"; "12" + 3 = "123")
System.out.println("Result: " + (1 + 2)); // Prints "Result: 3" (parentheses evaluate first)

When a primitive value is concatenated with a string, the primitive is converted to its string representation (e.g., true becomes "true", 5 becomes "5"). If an object reference is concatenated, its toString() method is invoked; if the reference is null, the literal four-character text "null" is appended.


5. Relational Comparison Operators

Java provides four relational operators that test order comparisons between two operands:

  • < : Less than
  • <= : Less than or equal to
  • > : Greater than
  • >= : Greater than or equal to

Strict Rules for Relational Operators

  1. Always Return Primitive boolean: Every relational comparison evaluates strictly to true or false.
  2. Numeric Operands Only: Relational operators can only be applied to numeric primitive types (byte, short, char, int, long, float, double).
  3. No Boolean or Reference Comparison: Applying <, <=, >, or >= to boolean variables, strings, or object references causes a compile-time error:
int age = 21;
boolean canVote = age >= 18; // Valid: true

char letter = 'B'; // Unicode 66
boolean isAfterA = letter > 'A'; // Valid: 66 > 65 evaluates to true

// COMPILE ERRORS: bad operand types for binary operator '>'
// boolean bad1 = true > false;
// boolean bad2 = "Apple" < "Banana";

When comparing operands of different numeric types, binary numeric promotion widens the narrower operand before evaluating the comparison:

System.out.println(10.0 > 10); // false (int 10 promoted to double 10.0; 10.0 > 10.0 is false)
System.out.println(10.0 >= 10); // true (10.0 >= 10.0 is true)

6. Equality Operators: Value Equality vs. Reference Identity

Java provides two equality operators: equal-to (==) and not-equal-to (!=). Their evaluation behavior depends fundamentally on whether they compare primitives or object references.

1. Primitive Equality: Value Comparison

When applied to primitive data types, == compares the raw binary values stored in memory. Operands of different numeric types undergo binary numeric promotion before comparison:

int x = 25;
double y = 25.0;
System.out.println(x == y); // true (x is widened to double 25.0; 25.0 == 25.0 is true)

char ch = 'A'; // Unicode 65
System.out.println(ch == 65); // true (ch is promoted to int 65; 65 == 65 is true)

boolean b1 = true;
boolean b2 = false;
System.out.println(b1 == b2); // false (compares boolean truth values)

2. Reference Equality: Memory Address Comparison

When applied to object reference variables (such as String, arrays, and class instances), == tests reference identity—whether both reference variables point to the exact same memory address on the Java Heap:

String s1 = new String("Java");
String s2 = new String("Java");
String s3 = s1;

System.out.println(s1 == s2);      // false (distinct heap objects at different memory addresses)
System.out.println(s1 == s3);      // true (both variables hold the identical heap reference pointer)
System.out.println(s1.equals(s2)); // true (compares character sequence contents)

[!IMPORTANT] Exam Trap: == vs .equals(): Never use == to compare text contents of strings. The == operator verifies whether two references point to the same memory location, while .equals() inspects the underlying character values.


7. Unconditional vs. Short-Circuit Logical Operators

Java supports two distinct sets of logical operators for evaluating boolean expressions:

  • Unconditional Operators: & (Logical AND), | (Logical OR), ^ (Logical XOR), ! (Logical Complement)
  • Short-Circuit Operators: && (Conditional AND), || (Conditional OR)

Truth Table for Boolean Operators

pqp & q / p && qp &#124; q / p &#124;&#124; qp ^ q (XOR)!p (NOT)
truetruetruetruefalsefalse
truefalsefalsetruetruefalse
falsetruefalsetruetruetrue
falsefalsefalsefalsefalsetrue

Unconditional Logical Operators (&, |)

The single ampersand & and single pipe | are unconditional operators. When evaluating left & right or left | right, both operands are always evaluated, even when the left operand has already determined the final truth value of the expression:

int count = 0;
boolean flag = (5 > 10) & (++count > 0); // 5 > 10 is false, but ++count is STILL evaluated
System.out.println("flag=" + flag + ", count=" + count); // flag=false, count=1

Short-Circuit Logical Operators (&&, ||)

Short-circuit operators optimize performance and provide safety by bypassing evaluation of the right-hand operand whenever the left-hand operand decisively establishes the outcome:

  1. Short-Circuit AND (&&): If the left operand evaluates to false, the overall expression can never be true. The right operand is completely skipped.
  2. Short-Circuit OR (||): If the left operand evaluates to true, the overall expression is guaranteed to be true. The right operand is completely skipped.
int score = 0;
boolean check = (5 > 10) && (++score > 0); // 5 > 10 is false -> ++score is SKIPPED!
System.out.println("check=" + check + ", score=" + score); // check=false, score=0

The Null Safety Guard Idiom

The most vital real-world and examination application of short-circuit evaluation is the null safety guard. By testing for null on the left of &&, safe code prevents fatal NullPointerException crashes:

String text = null;

// SAFE: Short-circuits when text == null; text.length() is NEVER invoked!
if (text != null && text.length() > 0) {
    System.out.println("Valid text");
}

// FATAL RUNTIME ERROR: Unconditional & evaluates both sides, crashing with NullPointerException!
// if (text != null & text.length() > 0) { ... }

Similarly, short-circuit operators safeguard against ArrayIndexOutOfBoundsException when validating indices before accessing array slots:

int[] data = { 10, 20, 30 };
int idx = 3;
if (idx >= 0 && idx < data.length && data[idx] > 0) {
    System.out.println("Positive element");
}
// Safe: idx < data.length is false (3 < 3 is false), data[3] is never evaluated!
Loading diagram...
Short-Circuit vs. Unconditional Logical Evaluation Mechanics
Test Your Knowledge

What is printed to the console when the following Java statements are executed?

int a = 14;
int b = 4;
String res = a / b + " " + (double)(a / b) + " " + ((double)a / b);
System.out.println(res);

A
B
C
D
Test Your Knowledge

What is printed after executing the following Java code snippet?

int r1 = -22 % 5;
int r2 = 22 % -5;
double d = -18.0 / 0.0;
System.out.println(r1 + "," + r2 + "," + d);

A
B
C
D
Test Your Knowledge

Consider the following Java program fragment:

int p = 8;
int q = 12;
boolean flag1 = (p > 10) && (++q > 12);
boolean flag2 = (p < 10) || (++q > 12);
System.out.println("q=" + q + ", flag1=" + flag1 + ", flag2=" + flag2);
What is the resulting console output?

A
B
C
D