JavaCommon Mistakes

Common Mistakes

Every Java developer runs into the same handful of gotchas at some point — most of them don’t cause a compile error, they cause a confusing bug or crash later, often far away from the line that actually caused it. Recognizing them ahead of time saves hours of debugging.

Mistake

Why it bites

What to do instead

Using == to compare objects or Strings

== compares references (are these the same object in memory?), not content. Two equal-looking Strings created differently can be ==-unequal.

Use .equals() to compare object content; reserve == for primitives and for deliberate identity checks (null, enum values).

Overriding equals() without hashCode()

Breaks the equals/hashCode contract, so equal objects can land in different hash buckets — HashMap/HashSet silently fail to find entries that "should" be there.

Always override both together (or generate both with your IDE, or use a record).

Mutable static fields shared across the app

A static field is shared by every caller everywhere, so one part of the app mutating it affects every other part — including concurrent threads racing on it.

Prefer instance fields; if state truly must be shared, make it immutable or explicitly manage synchronization.

Catching Exception (or Throwable) too broadly

Swallows bugs you never intended to handle — a NullPointerException from an unrelated line gets silently caught alongside the specific exception you meant to catch.

Catch the narrowest exception type that describes what you can actually recover from.

Not closing resources (pre-try-with-resources style)

Forgetting to close a file, socket, or DB connection in a finally block — or forgetting the finally block entirely — leaks a limited resource until the app runs out.

Use try-with-resources for anything AutoCloseable; let the compiler and runtime guarantee the close.

NullPointerException from an unboxed wrapper

Autoboxing quietly unboxes Integer/Long/etc. to a primitive when needed; if the wrapper is null, that unboxing throws NullPointerException with no cast or .equals() call in sight.

Be deliberate about where null can flow into wrapper types used in arithmetic or comparisons; consider Optional for values that may be absent.

Modifying a collection while iterating over it

Calling list.remove(x) inside a for-each loop over list throws ConcurrentModificationException — the iterator detects the list changed underneath it.

Use Iterator.remove(), Collection.removeIf(...), or build a new collection instead of mutating one you're iterating.

Comparing floating-point numbers with ==

float/double arithmetic accumulates rounding error, so two values that are mathematically equal can differ in their least-significant bits.

Compare with a tolerance: Math.abs(a - b) < epsilon, or use BigDecimal when exactness matters (e.g. money).

== vs. equals(), in code

A classic surprise with String literals vs. new String()

Java
String a = "hello";
String b = "hello";
String c = new String("hello");

System.out.println(a == b);       // true  — same interned literal
System.out.println(a == c);       // false — c is a distinct object
System.out.println(a.equals(c));  // true  — same content
ConcurrentModificationException, in code

Wrong: mutating a list during a for-each

Java
List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Carol"));

for (String name : names) {
    if (name.startsWith("B")) {
        names.remove(name); // throws ConcurrentModificationException
    }
}

Right: removeIf(), or an explicit Iterator

Java
names.removeIf(name -> name.startsWith("B"));

// or, with explicit control over the iteration:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("B")) {
        it.remove(); // safe — goes through the iterator itself
    }
}
NullPointerException from unboxing

A null wrapper blowing up on unboxing

Java
Map<String, Integer> scores = new HashMap<>();
int score = scores.get("Alice"); // scores.get returns null (key missing)
// NullPointerException — Java tries to unbox null into an int
These are exactly the bugs that only show up in production
Most mistakes on this page compile cleanly and pass a casual test run, because the failure condition (a specific input, a race, a collection shape) doesn’t show up until later — that’s what makes them worth memorizing rather than relying on the compiler to catch.