Arithmetic, and What % Is Really For
The five arithmetic operators are +, -, *, / and %. Four of them behave exactly as they did in school. Division does not, and the modulus operator is more useful than most beginners realise.
As covered in the previous lesson, / between two whole numbers throws away the remainder: 10 / 3 is 3. The % operator gives you back exactly what division discarded — 10 % 3 is 1. Together they let you split a quantity into whole units plus a leftover, which is the basis of a surprising number of programs: converting 250 minutes into hours and minutes, breaking a rupee amount into notes, or laying items out in rows of four.
The two uses of % you will write most often are testing divisibility and cycling through a range. n % 2 == 0 tests whether n is even. i % arr.length wraps an index back to the start once it runs off the end, which is how you rotate through a list forever without an if-statement.
One difference from mathematics: in Java the sign of % follows the left-hand operand, so -7 % 3 is -1, not 2. If you are using % to compute an array index from a value that might be negative, that negative result will cause an ArrayIndexOutOfBoundsException. Guard against it or use Math.floorMod(), which always returns a result with the sign of the divisor.
int a = 10, b = 3;
System.out.println(a + b); // 13
System.out.println(a - b); // 7
System.out.println(a * b); // 30
System.out.println(a / b); // 3 <- remainder discarded
System.out.println(a % b); // 1 <- the discarded remainder
// Splitting a quantity: 250 minutes -> hours and minutes
int totalMinutes = 250;
int hours = totalMinutes / 60; // 4
int minutes = totalMinutes % 60; // 10
System.out.println(hours + "h " + minutes + "m"); // 4h 10m
// Testing divisibility
int n = 18;
System.out.println(n % 2 == 0); // true — even
System.out.println(n % 3 == 0); // true — divisible by 3
// Negative operands: sign follows the LEFT side
System.out.println(-7 % 3); // -1
System.out.println(Math.floorMod(-7, 3)); // 2 — safe for array indices - Integer division by zero throws
ArithmeticException: / by zeroand stops your program. Floating-point division by zero does not throw —10.0 / 0quietly producesInfinity, and0.0 / 0.0producesNaN(not a number). ANaNthen poisons every calculation it touches, and — unlike anything else in Java —NaN == NaNisfalse. Test for it withDouble.isNaN(x).
Assignment Shortcuts, and the ++ Puzzle
total = total + 5 is common enough that Java gives you total += 5, and the same shorthand exists for every arithmetic operator. These are not merely shorter — they also perform a silent narrowing cast, which is occasionally handy and occasionally a trap. byte b = 10; b = b + 5; does not compile, because b + 5 is an int. But b += 5; compiles perfectly, because the compound form inserts a cast back to byte for you. That also means int i = 5; i += 3.7; compiles and leaves i as 8, silently discarding the fraction.
++ and -- add or subtract one. Written before the variable (++i, prefix) or after it (i++, postfix), they change the variable identically. The difference is only in the value the expression produces: prefix increments first and gives you the new value, postfix gives you the old value and then increments.
As a statement on its own line, i++; and ++i; are interchangeable and you should not lose sleep over which to write. The difference only bites when you use the result in the same expression, and the honest advice is: do not. Code like arr[i++] = i; is a puzzle, not a program, and reviewers will ask you to rewrite it.
One expression is worth seeing once because it appears in exams. x = x++; leaves x unchanged. Java evaluates x++ to the old value, then increments x, then the assignment overwrites x with the saved old value. The increment is thrown away.
int x = 10;
x += 5; // 15
x -= 3; // 12
x *= 2; // 24
x /= 4; // 6
x %= 4; // 2
// Compound assignment hides a cast
byte b = 10;
// b = b + 5; // compile error: possible lossy conversion from int to byte
b += 5; // fine — an implicit (byte) cast is inserted
int i = 5;
i += 3.7; // compiles! i is now 8 — the .7 is silently dropped
// ---- prefix vs postfix ----
int p = 5;
System.out.println(p++); // prints 5, then p becomes 6
System.out.println(p); // 6
int q = 5;
System.out.println(++q); // q becomes 6, then prints 6
// The exam classic
int z = 5;
z = z++;
System.out.println(z); // 5 — the increment was overwritten +does double duty: numeric addition and String concatenation. It is evaluated left to right, so1 + 2 + "a"gives"3a"(numbers added first), while"a" + 1 + 2gives"a12"(the first+made it a String). This is whySystem.out.println("Total: " + a + b)so often prints two digits glued together instead of a sum. Wrap the arithmetic in brackets.
Comparison, and Why == Lies About Strings
The comparison operators >, <, >=, <=, == and != each produce a boolean. On primitives they do exactly what you expect. On objects, == does something different from what almost every beginner assumes, and this is the single most-asked Java interview question in India.
Remember that a reference variable holds an address, not the object. So == on two objects asks "are these two variables pointing at the same object in memory?" — not "do these two objects contain the same data?". To compare contents you call .equals(), which classes like String override to compare character by character.
What makes this genuinely dangerous is that == often appears to work on Strings. Java keeps a string constant pool: when the compiler sees a string literal such as "hello" it stores one copy and reuses it everywhere that literal appears. So two variables assigned the literal "hello" really do point at the same object, and == returns true. Your code passes every test you write by hand.
Then the string arrives from somewhere else — a Scanner, a file, a database, a web form, or new String("hello"). Now it is a fresh object in memory with identical contents, == returns false, and your login check fails for reasons that make no sense while you are staring at two identical strings on screen. The rule has no exceptions worth learning: always use .equals() to compare strings.
// On primitives, == compares values. No surprises.
System.out.println(5 > 3); // true
System.out.println(5 == 5); // true
System.out.println(5 != 3); // true
// ---- On Strings, == compares identity ----
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true — both point at the pooled literal
System.out.println(s1.equals(s2)); // true
String s3 = new String("hello");
System.out.println(s1 == s3); // FALSE — a separate object, same contents
System.out.println(s1.equals(s3)); // true — this is the answer you wanted
// The same thing happens with input you did not type as a literal
// Scanner sc = new Scanner(System.in);
// String typed = sc.nextLine(); // user types: hello
// typed == "hello" -> false
// typed.equals("hello") -> true
// Useful String comparisons
"HELLO".equalsIgnoreCase("hello"); // true
"apple".compareTo("banana"); // negative — apple sorts first
// Null-safe: put the literal first, or use Objects.equals
String maybeNull = null;
// maybeNull.equals("hi") -> NullPointerException
"hi".equals(maybeNull); // false, no exception
java.util.Objects.equals(maybeNull, "hi"); // false, no exception - The same identity-versus-contents rule applies to every object, not just String.
==on twoIntegerobjects, twoArrayLists or two of your own objects compares addresses. Later lessons show how to write your ownequals()so that contents comparison works for your classes too.
Logical Operators and Short-Circuiting
&& (and), || (or) and ! (not) combine boolean values. && is true only when both sides are true; || is true when at least one side is; ! flips a boolean.
The important behaviour is short-circuiting. Java evaluates the left side first, and if that already settles the answer it never evaluates the right side at all. For &&, a false left side means the whole expression is false regardless, so the right side is skipped. For ||, a true left side means the answer is true, so the right side is skipped.
This is not a performance footnote — it is a safety mechanism you will use constantly. if (name != null && name.length() > 0) is safe precisely because when name is null the second condition never runs. Reverse the order and you get a NullPointerException. The same pattern guards array access: if (i < arr.length && arr[i] == target).
Java also has & and |, which do the same logic without short-circuiting — both sides always run. You almost never want them on booleans, and using them by mistake in a null check removes the protection entirely. Their real job is bitwise arithmetic on integers, described below.
boolean hasPaid = true;
boolean isEnrolled = false;
System.out.println(hasPaid && isEnrolled); // false
System.out.println(hasPaid || isEnrolled); // true
System.out.println(!isEnrolled); // true
// ---- Short-circuiting as a null guard ----
String name = null;
if (name != null && name.length() > 0) { // safe: length() never runs
System.out.println("Hello, " + name);
} else {
System.out.println("No name given");
}
// if (name.length() > 0 && name != null) // NullPointerException — wrong order
// if (name != null & name.length() > 0) // NullPointerException — & does not skip
// Guarding an array index the same way
int[] marks = {90, 85, 78};
int i = 5;
if (i < marks.length && marks[i] > 50) { // safe: no ArrayIndexOutOfBounds
System.out.println("Pass");
}
// Combining conditions — brackets make intent obvious
int age = 20;
boolean eligible = (age >= 18 && age <= 60) || hasPaid; - Because the right-hand side may be skipped, never hide something with a side effect there.
if (check() && count++ > 0)incrementscountonly sometimes, and tracking down why a counter is wrong takes far longer than writing two clear lines.
The Ternary Operator, Bitwise Operators and Precedence
The ternary operator condition ? valueIfTrue : valueIfFalse is the only operator in Java taking three operands. It is an expression, so it produces a value you can assign — which is exactly what makes it useful, because an if statement cannot be assigned to anything. One ternary in place of a four-line if-else is a readability win. Two ternaries nested inside each other is a readability loss; write the if-else instead.
The bitwise operators work on the individual bits of integers: &, |, ^ (exclusive or), ~ (flip all bits), << (shift left) and >> (shift right). You will not need them for everyday application code, but they show up in DSA problems — checking whether a number is a power of two, toggling flags, or the classic "find the one number that appears once when every other appears twice", solved by XOR-ing everything together.
Finally, operator precedence decides what binds tighter when you mix operators. The full table has fifteen levels and memorising it is a poor use of your time. Learn the handful below and put brackets around anything else — brackets cost nothing and remove all doubt for the next person reading your code.
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
int a = 47, b = 92;
int max = (a > b) ? a : b; // 92
// Fine in a print statement
System.out.println("You have " + count + " item" + (count == 1 ? "" : "s"));
// Nested ternaries — technically legal, genuinely hard to read. Use if-else.
// String grade = m >= 90 ? "A" : m >= 80 ? "B" : m >= 70 ? "C" : "F";
// ---- Bitwise, for the DSA questions that use them ----
System.out.println(6 & 3); // 2 0110 & 0011 = 0010
System.out.println(6 | 3); // 7 0110 | 0011 = 0111
System.out.println(6 ^ 3); // 5 0110 ^ 0011 = 0101
System.out.println(6 << 1); // 12 shift left = multiply by 2
System.out.println(6 >> 1); // 3 shift right = divide by 2
// n is a power of two exactly when it has a single 1 bit
int n = 16;
System.out.println(n > 0 && (n & (n - 1)) == 0); // true ++--!and casts bind tightest- then
*/% - then
+- - then
<><=>= - then
==!= - then
&&, then|| - then the ternary
? : - and
=+=-=bind loosest of all
- The precedence rule that catches people is that
&&binds tighter than||. Soa || b && cmeansa || (b && c), not(a || b) && c. If a condition mixes the two, add the brackets even where they are technically redundant.
