Java's bug surface is older than most. The language has accumulated decades of best practices, but legacy patterns and enterprise frameworks keep some bug classes alive. NullPointerException is still the modal cause of crashes, despite `Optional` having existed for a decade.
The recurring shapes: NPEs from a method that returns null instead of `Optional.empty()`, race conditions in code that uses `HashMap` where `ConcurrentHashMap` was needed, resource leaks before try-with-resources became the norm, and over-broad `catch (Exception e)` swallowing real bugs.
Modern Java (records, sealed classes, pattern matching, virtual threads) makes a lot of older patterns obsolete. The bug surface evolves with the migration; teams that haven't moved still see the old shapes.
Common pitfalls
Null returns instead of Optional
A method that sometimes returns null and sometimes a value forces every caller to remember. Returning `Optional<T>` makes the choice explicit at the type level.
Non-thread-safe collections
`HashMap` is not safe for concurrent modification. Under load it can corrupt itself. Use `ConcurrentHashMap` or external synchronization.
Catching Exception
`catch (Exception e)` swallows everything including bugs. Catch the specific subclasses you expect.