Common Mistakes in Java
Introduction
Even experienced developers can fall into traps when writing Java code. The language's object-oriented nature, type system, and memory management model have nuances that can lead to subtle bugs or severe performance issues if misunderstood.
This guide explores the most frequent mistakes Java developers make, why they happen at a JVM level, and how to fix them.
1. Comparing Objects Using == Instead of .equals()
The Mistake
Using the == operator to compare the contents of objects like String, Integer, or custom classes.
String a = new String("hello");
String b = new String("hello");
if (a == b) { // Evaluates to false
System.out.println("Strings are equal");
}
The Fix
In Java, == compares object references (memory addresses), while .equals() compares object values (state). Always use .equals() for value comparison.
if (a.equals(b)) { // Evaluates to true
System.out.println("Strings are equal");
}
Note: For primitive types (int, double, etc.), == is the correct way to compare values.
2. Ignoring NullPointerException (NPE)
The Mistake
Assuming an object is instantiated without checking for null, leading to a runtime crash.
String name = getUserName();
// If getUserName() returns null, this throws an NPE
if (name.equals("admin")) {
// ...
}
The Fix
Either flip the comparison if comparing against a constant, or use java.util.Optional to handle absent values gracefully.
// Fix 1: Yoda condition
if ("admin".equals(name)) {
// ...
}
// Fix 2: Explicit null check
if (name != null && name.equals("admin")) {
// ...
}
3. Modifying a Collection While Iterating
The Mistake
Removing or adding elements to a List or Set inside a standard for-each loop. This throws a ConcurrentModificationException.
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
for (String name : names) {
if (name.equals("Bob")) {
names.remove(name); // Throws ConcurrentModificationException
}
}
The Fix
Use an Iterator explicitly, use the removeIf() method (Java 8+), or iterate backwards if using a traditional for-loop.
// Java 8+ approach
names.removeIf(name -> name.equals("Bob"));
// Iterator approach (Java 7 and below)
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("Bob")) {
iterator.remove();
}
}
4. Forgetting break in switch Statements
The Mistake
Omitting the break keyword at the end of a case block causes execution to "fall through" to the next case.
int day = 2;
switch (day) {
case 1: System.out.println("Monday");
case 2: System.out.println("Tuesday"); // Executes this
case 3: System.out.println("Wednesday"); // And this!
}
The Fix
Always include a break statement unless the fall-through is intentional (which should be documented). Alternatively, use the enhanced switch expressions introduced in modern Java (Java 14+).
// Modern Java switch expression (No break needed)
switch (day) {
case 1 -> System.out.println("Monday");
case 2 -> System.out.println("Tuesday");
case 3 -> System.out.println("Wednesday");
}
5. Memory Leaks with Unclosed Resources
The Mistake
Opening files, database connections, or network sockets and forgetting to close them. Even though Java has a Garbage Collector, external OS resources are not automatically reclaimed and will eventually cause resource exhaustion.
FileInputStream fis = new FileInputStream("data.txt");
// Read data...
// If an exception occurs, fis.close() might never be called!
fis.close();
The Fix
Use the try-with-resources statement (introduced in Java 7). Any class that implements AutoCloseable will be automatically closed at the end of the block, even if an exception is thrown.
try (FileInputStream fis = new FileInputStream("data.txt")) {
// Read data...
} catch (IOException e) {
e.printStackTrace();
} // fis is automatically closed here
Summary
Avoiding these common pitfalls will drastically improve the stability of your Java applications. Always remember to:
- Understand the difference between value and reference equality.
- Program defensively against null values.
- Handle collections safely during iteration.
- Manage OS resources effectively using
try-with-resources.