Everything, Everywhere
Verified Specification | Standardized Formulas | Instant Precision
Secure & Private (Zero Data Retention) Free Access • No Sign-Up

Java Anti-Patterns: What NOT To Do

Great software engineers are defined as much by what they avoid as what they write. Here are the 4 deadly Java anti-patterns.

1. The "Pokemon" Exception Handler (Gotta Catch 'Em All)

Never, ever write an empty catch (Exception e) {} block. This is called "swallowing an error." If your code crashes, you will have ZERO log output, leaving you debugging in the dark for hours.

// ❌ NEVER DO THIS:
try { connectDatabase(); } catch (Exception e) { /* silently ignored */ }

// ✅ ALWAYS LOG OR THROW:
try { connectDatabase(); } catch (SQLException e) { logger.error("DB failed", e); }

2. Returning null Instead of Empty Collections

If a method returns a list of items and there are none, never return null. Return an empty list (Collections.emptyList()). This saves whoever calls your code from checking for null and prevents the dreaded NullPointerException.

📋 Copy Java Defensive Code Review Standards Snippet
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

// ANTI-PATTERN: catch (Exception e) {}
// PRODUCTION STANDARD: Explicit logging and context wrapping
try {
    processData();
} catch (IOException e) {
    // Preserve root cause exception and context
    throw new RuntimeException("Storage pipeline failed to process records", e);
}

// ANTI-PATTERN: return null;
// PRODUCTION STANDARD: Return empty collections or Optional
public List<String> getTags() {
    return tags.isEmpty() ? Collections.emptyList() : List.copyOf(tags);
}

⚠️ 5 Fatal Traps & Engineering Pitfalls

Trap #1: Swallowing Exceptions with Empty Catch Blocks
Writing catch (Exception e) {} silently consumes fatal runtime failures without logging or rethrowing. The application continues running in an invalid corrupted state, making root-cause diagnostics in production logs completely impossible.
Trap #2: Returning Null Instead of Empty Collections or Optional
Returning null from a method that retrieves lists, arrays, or domain entities forces every caller to write defensive null checks. Forgetting a check results in ubiquitous NullPointerException crashes. Return Collections.emptyList() or Optional<T>.
Trap #3: Using Legacy java.util.Date and Calendar
Legacy date classes are mutable, confusing (months are 0-indexed, so 0 is January), and fundamentally thread-unsafe. Always migrate to modern java.time (Instant, LocalDate, ZonedDateTime) introduced in Java 8.
Trap #4: Broken Double-Checked Locking Without volatile
Implementing lazy-loaded singletons with double-checked locking without marking the instance field volatile allows other CPU threads to read partially constructed objects due to compiler instruction reordering.
Trap #5: Calling System.exit() Inside Reusable Libraries
Invoking System.exit(0) inside a library or service abruptly halts the host JVM process without allowing web servers or sibling background workers to perform clean shutdowns, close database pools, or flush file buffers.

💬 Frequently Asked Questions

Why is catching java.lang.Throwable or generic Exception considered an anti-pattern?
Catching Throwable intercepts fatal JVM errors like OutOfMemoryError and StackOverflowError that an application cannot recover from. Catching generic Exception obscures specific failure modes and prevents proper error handling.
Why should methods return empty collections rather than null?
Returning empty collections (e.g. Collections.emptyList()) allows callers to iterate with for-each loops or call stream() directly without risking NullPointerExceptions or cluttering code with null guards.
What makes java.time superior to legacy Date and Calendar classes?
java.time classes are strictly immutable, thread-safe, follow ISO-8601 calendar standards, use sensible 1-based indexing for months, and separate machine timestamps (Instant) from human calendar dates (LocalDate).
What is the volatile keyword and why is it required in double-checked locking?
The volatile keyword establishes a happens-before memory relationship, guaranteeing all threads observe writes immediately and preventing compiler instruction reordering from publishing references to partially initialized objects.
Why is System.exit() dangerous inside shared services and web containers?
System.exit() kills the entire operating system process hosting the JVM. In microservices, servlet containers, or plugin architectures, this terminates all concurrent tenant threads and drops active user connections.
Sponsored Utility
While You're Here
Sponsored Recommendations
Advertisement