☕ Java
Arithmetic Operators
Arithmetic operators perform mathematical operations on numeric values. Java's arithmetic operators look simple — but integer division, modulo with negatives, overflow behavior, and floating-point precision all have specific rules that trip up developers at every level.
The Five Basic Arithmetic Operators
Java has five binary arithmetic operators that work on all numeric primitive types:
Java
int a = 17, b = 5;
System.out.println(a + b); // 22 — addition
System.out.println(a - b); // 12 — subtraction
System.out.println(a * b); // 85 — multiplication
System.out.println(a / b); // 3 — integer division (truncates)
System.out.println(a % b); // 2 — modulo (remainder)
// With doubles:
double x = 17.0, y = 5.0;
System.out.println(x + y); // 22.0
System.out.println(x - y); // 12.0
System.out.println(x * y); // 85.0
System.out.println(x / y); // 3.4 — floating-point division
System.out.println(x % y); // 2.0 — floating-point remainderInteger Division — Truncation, Not Rounding
When both operands are integer types, / performs integer division: the result is truncated toward zero, discarding any remainder. This is one of the most common sources of subtle bugs in Java.
Java
// Integer division truncates toward zero:
System.out.println(7 / 2); // 3 — not 3.5
System.out.println(7 / 4); // 1 — not 1.75
System.out.println(-7 / 2); // -3 — truncated toward zero, not -4
System.out.println(1 / 3); // 0 — less than 1, truncates to 0
// The silent bug — computing an average wrong:
int total = 7, count = 2;
double average = total / count; // 3.0 — WRONG: integer division first
double correct = (double) total / count; // 3.5 — correct: cast before dividing
// Other ways to force floating-point division:
double d1 = total / (double) count; // 3.5
double d2 = (double) total / count; // 3.5
double d3 = 1.0 * total / count; // 3.5 — multiply by 1.0 first
// Division by zero:
int x = 5 / 0; // throws ArithmeticException: / by zero at runtime
double y = 5.0 / 0.0; // Infinity — floating-point division by zero does NOT throw
double z = 0.0 / 0.0; // NaN — not a number
System.out.println(Double.isInfinite(y)); // true
System.out.println(Double.isNaN(z)); // trueModulo — Remainder Operator
The % operator returns the remainder after division. Its behavior with negative numbers is specific in Java: the sign of the result matches the sign of the dividend (left operand).
Java
// Basic modulo:
System.out.println(10 % 3); // 1 — 10 = 3*3 + 1
System.out.println(10 % 5); // 0 — 10 = 5*2 + 0
System.out.println(7 % 10); // 7 — 7 = 10*0 + 7
// Negative dividend — result has same sign as dividend:
System.out.println(-10 % 3); // -1 — result is negative (matches -10)
System.out.println(10 % -3); // 1 — result is positive (matches 10)
System.out.println(-10 % -3); // -1 — result is negative (matches -10)
// Floating-point modulo:
System.out.println(10.5 % 3.0); // 1.5
System.out.println(5.5 % 2.2); // 1.1000000000000005 — floating-point imprecision
// Common uses of modulo:
// Check if even or odd:
int n = 42;
if (n % 2 == 0) System.out.println("even");
else System.out.println("odd");
// Cycle through values 0 to N-1:
int[] slots = new int[7];
for (int i = 0; i < 20; i++) {
slots[i % 7]++; // cycles: 0,1,2,3,4,5,6,0,1,2...
}
// Ensure positive modulo (when dividend may be negative):
int mod = ((n % 7) + 7) % 7; // always 0-6, even if n is negativeIncrement and Decrement Operators
++ and -- add or subtract 1 from a variable. They exist in two forms — prefix and postfix — that differ in when the value is updated relative to when it's read.
Java
// Prefix — increment THEN return new value:
int a = 5;
int b = ++a; // a becomes 6, then b = 6
System.out.println(a); // 6
System.out.println(b); // 6
// Postfix — return current value THEN increment:
int x = 5;
int y = x++; // y = 5 (original), then x becomes 6
System.out.println(x); // 6
System.out.println(y); // 5
// Decrement — same logic with --:
int c = 10;
System.out.println(--c); // 9 — prefix: decrement then return
System.out.println(c--); // 9 — postfix: return then decrement
System.out.println(c); // 8
// In loops — prefix and postfix make no difference when used alone:
for (int i = 0; i < 5; i++) { } // same as ++i here
for (int i = 0; i < 5; ++i) { } // identical behavior in this context
// Where it matters — inside expressions:
int n = 3;
int result = n++ * 2; // result = 3*2 = 6, then n = 4
int result2 = ++n * 2; // n becomes 5 first, result2 = 5*2 = 10
// Classic confusion:
int i = 5;
i = i++; // i is still 5! postfix returns original value before increment
// Sequence: temp = i (5), i = i+1 (6), i = temp (5)Arithmetic with Mixed Types
When operands have different types, Java automatically promotes the smaller type to match the larger one before performing the operation.
Java
// int + long = long:
int i = 100;
long l = 200L;
long result = i + l; // i promoted to long
// int + double = double:
int x = 5;
double d = 2.5;
double r = x + d; // x promoted to double: 7.5
// byte/short arithmetic always produces int:
byte b1 = 10, b2 = 20;
// byte sum = b1 + b2; // COMPILE ERROR
int sum = b1 + b2; // 30
// Compound arithmetic preserves type:
byte b = 10;
b += 5; // equivalent to b = (byte)(b + 5) — implicit cast in compound op
b++; // works — ++ on byte stays byte (implicit cast)
// char arithmetic produces int:
char c = 'A';
int code = c + 1; // 66
char next = (char)(c + 1); // 'B' — need explicit cast back to charOverflow and Underflow
Java integer arithmetic silently overflows — when a result exceeds the type's range, it wraps around with no exception and no warning. Floating-point uses Infinity and NaN instead.
Java
// Integer overflow — wraps silently:
int max = Integer.MAX_VALUE; // 2,147,483,647
System.out.println(max + 1); // -2,147,483,648 — wrapped to MIN_VALUE
int min = Integer.MIN_VALUE; // -2,147,483,648
System.out.println(min - 1); // 2,147,483,647 — wrapped to MAX_VALUE
// Long overflow — same wrapping:
long lMax = Long.MAX_VALUE;
System.out.println(lMax + 1); // Long.MIN_VALUE
// Detect overflow safely with Math methods (Java 8+):
try {
int safe = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Overflow detected"); // thrown instead of wrapping
}
Math.subtractExact(a, b); // safe subtraction
Math.multiplyExact(a, b); // safe multiplication
// Floating-point — uses special values instead of wrapping:
System.out.println(Double.MAX_VALUE * 2); // Infinity
System.out.println(-Double.MAX_VALUE * 2); // -Infinity
System.out.println(Double.MAX_VALUE + 1); // Double.MAX_VALUE — too small a change
System.out.println(0.0 / 0.0); // NaN
// NaN comparisons are always false:
double nan = Double.NaN;
System.out.println(nan == nan); // false — NaN is not equal to itself
System.out.println(nan > 0); // false
System.out.println(Double.isNaN(nan)); // true — correct way to checkString Concatenation with +
The + operator is overloaded for String concatenation. When either operand is a String, + concatenates rather than adds. The left-to-right evaluation order means the placement of Strings in an expression matters significantly.
Java
// String + anything = String:
String s = "Value: " + 42; // "Value: 42"
String s2 = "Pi: " + 3.14; // "Pi: 3.14"
String s3 = "Flag: " + true; // "Flag: true"
// Left-to-right evaluation — order matters:
System.out.println("Sum: " + 1 + 2); // "Sum: 12" — "Sum:1" then concat 2
System.out.println("Sum: " + (1 + 2)); // "Sum: 3" — parens force int add first
System.out.println(1 + 2 + " sum"); // "3 sum" — int add first, then concat
// null concatenation — null becomes the String "null":
String name = null;
System.out.println("Name: " + name); // "Name: null"
// Compiler optimization — consecutive String literals are folded at compile time:
String s4 = "Hello" + " " + "World"; // compiled as "Hello World" — no runtime concat
// For repeated concatenation, use StringBuilder:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i).append(", ");
}
String result = sb.toString();Related Topics in Java Basics
Variables in Java
A variable is just a named box in memory that holds a value. Java is strict about what goes in each box — you tell it the type upfront. Once you get this, the rest of Java clicks into place.
Data Types in Java
Java needs to know exactly what kind of data it's dealing with before it can store or process it. Integers, decimals, characters, true/false — each has its own type. Knowing which to use (and why) makes your programs efficient and bug-free.
Primitive Data Types
Java has eight primitive data types — the most basic building blocks for storing data. Unlike objects, primitives are stored directly in memory, making them fast and efficient. Understanding each type, its size, range, and when to use it is fundamental to writing correct Java programs.
Non-Primitive Data Types
Non-primitive data types — also called reference types — are everything beyond Java's eight primitives. Strings, arrays, classes, interfaces, enums, and records all fall into this category. They're more powerful than primitives, but they work differently in memory, comparison, and nullability. Understanding the distinction is essential for writing correct Java.