Java is a powerful and widely used programming language, but developers often encounter common pitfalls that can lead to bugs and performance issues. Understanding these bugs and their solutions can help improve code reliability and maintainability. Here are some of the most common Java bugs and how to fix them.
1. NullPointerException (NPE)
A NullPointerException occurs when trying to access a method or field of a null object.
Example Bug:
String text = null;
System.out.println(text.length()); // Throws NullPointerException
Fix:
Always check for null before accessing an object.
if (text != null) {
System.out.println(text.length());
} else {
System.out.println(“String is null”);
}
Alternatively, use Optional in Java 8+ to handle null values safely.
Optional.ofNullable(text).ifPresent(t -> System.out.println(t.length()));
2. Infinite Loops
Infinite loops occur when the termination condition is not met or improperly implemented.
Example Bug:
int i = 0;
while (i < 5) {
System.out.println(i);
// Missing i++ causes infinite loop
}
Fix:
Ensure the loop condition is properly updated.
int i = 0;
while (i < 5) {
System.out.println(i);
i++; // Correctly incrementing i
}
3. ArrayIndexOutOfBoundsException
Trying to access an array element outside its valid range results in this exception.
Example Bug:
int[] numbers = {1, 2, 3};
System.out.println(numbers[3]); // Throws ArrayIndexOutOfBoundsException
Fix:
Always check the array length before accessing an index.
if (index >= 0 && index < numbers.length) {
System.out.println(numbers[index]);
} else {
System.out.println(“Invalid index”);
}
4. Using == Instead of .equals() for String Comparison
The == operator checks object reference equality, not content equality.
Example Bug:
String s1 = “hello”;
String s2 = new String(“hello”);
System.out.println(s1 == s2); // false (compares memory locations)
Fix:
Use .equals() to compare string values.
System.out.println(s1.equals(s2)); // true (compares content)
5. Memory Leaks from Unclosed Resources
Not closing resources like file streams or database connections can cause memory leaks.
Example Bug:
FileReader fr = new FileReader(“data.txt”);
// Forgot to close file
Fix:
Use try-with-resources (Java 7+), which automatically closes resources.
try (FileReader fr = new FileReader(“data.txt”)) {
// Read file
} catch (IOException e) {
e.printStackTrace();
}
6. Concurrent Modification Exception
Modifying a collection while iterating over it can cause a ConcurrentModificationException.
Example Bug:
List<String> list = new ArrayList<>();
list.add(“A”);
list.add(“B”);
for (String item : list) {
if (item.equals(“A”)) {
list.remove(item); // Throws ConcurrentModificationException
}
}
Fix:
Use an iterator’s remove() method instead.
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals(“A”)) {
iterator.remove();
}
}
7. Forgetting to Override hashCode() When Overriding equals()
If hashCode() is not overridden when equals() is overridden, objects may not behave correctly in hash-based collections like HashSet or HashMap.
Example Bug:
class Person {
String name;
Person(String name) { this.name = name; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
}
Set<Person> people = new HashSet<>();
people.add(new Person(“Alice”));
System.out.println(people.contains(new Person(“Alice”))); // false (unexpected)
Fix:
Always override hashCode() when overriding equals().
@Override
public int hashCode() {
return Objects.hash(name);
}
8. Ignoring Exceptions
Catching an exception but not handling it can lead to hidden issues.
Example Bug:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
// Silent catch, no error message
}
Fix:
Log the exception or handle it appropriately.
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println(“Error: ” + e.getMessage());
}
9. Deadlocks in Multithreading
A deadlock occurs when two threads wait indefinitely for each other to release a lock.
Example Bug:
class Deadlock {
static final Object lock1 = new Object();
static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
synchronized (lock2) { // Locks in one order
System.out.println(“Thread 1”);
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
synchronized (lock1) { // Locks in reverse order
System.out.println(“Thread 2”);
}
}
}).start();
}
}
Fix:
Always acquire locks in a consistent order to prevent deadlocks.
synchronized (lock1) {
synchronized (lock2) {
System.out.println(“Thread Safe”);
}
}
10. Overlooking Integer Division Precision
Integer division in Java discards the decimal part, leading to incorrect results.
Example Bug:
int a = 5, b = 2;
System.out.println(a / b); // Outputs 2 instead of 2.5
Fix:
Use double for division.
double result = (double) a / b;
System.out.println(result); // Outputs 2.5
Conclusion
Java bugs often stem from common mistakes in handling null values, collections, multithreading, and resource management. By understanding these pitfalls and applying best practices, developers can write more robust, efficient, and maintainable Java applications.