Operators

Use Java's arithmetic, comparison, logical, bitwise and assignment operators correctly, including the remainder, increment and short-circuit rules.

Syntaxa + b a - b a * b a / b a % b i++ ++i i-- --i x += 5 a == b a != b a < b x && y x || y !x mask & flag a | b a ^ b ~a a << n a >> n a >>> n

Arithmetic

+ - * / % work as expected with two warnings. Division of two integers truncates toward zero (17 / 5 is 3), and integer division by zero throws ArithmeticException. The remainder % takes the sign of the left operand, so -7 % 3 is -1. When you use % to pick a shard or bucket from a hash code, which can be negative, use Math.floorMod, which is never negative for a positive divisor.

Ceiling division for page counts is (items + pageSize - 1) / pageSize, or Math.ceilDiv since Java 18.

Increment and decrement

i++ evaluates to the old value and then increments; ++i increments and evaluates to the new value. On a line of its own the difference does not matter. Inside a larger expression it does, which is a good reason not to put them inside larger expressions.

Comparison and logic

== != < > <= >= compare primitives by value. On objects, == compares references, so strings, wrappers and records need equals. && and || short-circuit: the right side is evaluated only if it can change the result. That is what makes user != null && user.isActive() safe. The single & and | on booleans evaluate both sides, so they would throw here. ! negates.

Bitwise and shifts

& | ^ ~ work on the bits of integers, and << >> >>> shift them. >> keeps the sign; >>> fills with zeros. You meet these in permission masks, feature flags packed into an integer, hashing and binary protocols. For anything readable, an EnumSet is usually the better choice.

Assignment and the conditional operator

+= -= *= /= %= and friends combine an operation with assignment and include a cast back to the variable's type, so int x = 10; x += 2.7; silently gives 12. The conditional cond ? a : b picks one of two values and has its own lesson.

String concatenation and precedence

+ with a String on either side becomes concatenation, evaluated left to right. 1 + 2 + "3" is "33", but "1" + 2 + 3 is "123". Multiplication binds tighter than addition, && tighter than ||, and comparison tighter than both. Add brackets whenever a reviewer would have to stop and think; they cost nothing.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Work out how many pages a paginated endpoint returns with ceiling division.
  • Choose a cache or database shard from a user ID hash with Math.floorMod.
  • Guard a property access with && so a missing request header does not throw NullPointerException.

More examples

Remainder with negative numbers

Using % on a negative hash gives a negative index, which throws ArrayIndexOutOfBoundsException when used to pick a shard. Math.floorMod always returns a value from 0 to shards - 1.

Example · java
void main() {
    IO.println(-7 % 3);
    IO.println(Math.floorMod(-7, 3));

    int shards = 4;
    int hash = -1_234_567;                        // hash codes can be negative
    IO.println("% gives " + (hash % shards));
    IO.println("floorMod gives " + Math.floorMod(hash, shards));
}
// Output:
// -1
// 2
// % gives -3
// floorMod gives 1

Bitwise permission flags and shifts

Each permission is one bit, | sets bits, & tests them and & ~ clears them. The brackets around (perms & WRITE) are required because != binds tighter than &.

Example · java
static final int READ = 1, WRITE = 2, DELETE = 4;

void main() {
    int perms = READ | WRITE;
    IO.println(Integer.toBinaryString(perms));
    IO.println("can write: " + ((perms & WRITE) != 0));
    IO.println("can delete: " + ((perms & DELETE) != 0));
    perms &= ~WRITE;                  // clear the WRITE bit
    IO.println("after revoke: " + perms);

    IO.println(1 << 10);              // 1024
    IO.println(-16 >> 2);             // sign kept
    IO.println(-16 >>> 28);           // zeros shifted in
}
// Output:
// 11
// can write: true
// can delete: false
// after revoke: 1
// 1024
// -4
// 15

Hidden casts and reference comparison

Compound assignment includes a narrowing cast, so the fraction disappears without a warning. == on objects checks identity, which is why strings need equals.

Example · java
void main() {
    int x = 10;
    x += 2.7;                         // means x = (int) (x + 2.7)
    IO.println(x);

    short s = 1;
    s += 1;                           // fine
    // s = s + 1;                     // compile error: int cannot be converted to short
    IO.println(s);

    String a = "ok";
    String b = new String("ok");
    IO.println((a == b) + " " + a.equals(b));
    IO.println(true || false && false);   // && first: true || (false && false)
}
// Output:
// 12
// 2
// false true
// true

Discussion

  • Be the first to comment on this lesson.