Year-End Discount: 10% OFF 1-year and 20% OFF 2-year subscriptions!

Home/Blog/Top 10 mistakes Java developers make - and how to fix them

Top 10 mistakes Java developers make - and how to fix them

Apr 02, 2021 - 7 min read
Joshua Ahn
editor-page-cover

Java is an object-oriented programming (OOP) language developed by Oracle. One of the most popular programming languages in the world, Java is used to create Android applications, web applications, games, and database-driven software.

Today, we’re taking a look at 10 common mistakes Java developers make as well as how to fix them.

Here’s what we’ll cover today:


Refresh your Java knowledge.

This path will help you become a modern, employable Java developer with incredibly valuable skills, ready to jump in and contribute to real-world projects.

Java for Programmers


Misusing mutable vs immutable objects

There’s a careful balance to strike when choosing between creating a mutable or immutable object. A mutable object can be modified after creation, whereas an immutable object cannot. While immutable objects offer benefits such as safety, simplicity, and reduced memory, they build up garbage collection when requiring a separate object for each distinct value.

Below are some key differences between mutable vs immutable objects to determine which to use upon object creation.

Mutable:

  • When changes are made to the object, no new object is formed.

  • Methods are provided to change the content of an object.

  • Both getter and setter methods are present.

  • Common examples include StringBuffer , StringBuilder, and java.util.date.

Immutable:

  • A new object is formed when an existing object is changed.

  • No methods are provided to change the content of an object.

  • Only getter methods are present. There are no setter methods.

  • Common examples include all legacy classes, wrapper classes, and String classes.


Confusing =, ==, and .equals()

Confusing these three operators is an easy mistake to make, especially between the latter two. Remember that = is used for assignment. == is a referential equality comparison, meaning it checks whether both objects point to the same memory location. .equals() evaluates whether the two objects are equal in value.

As a rule of thumb, stick to .equals() when comparing objects, especially strings.

1 String a = "hello";
2 String b = "world";
3 
4 if ( (a+b).equals("helloworld") )
5 {
6    System.out.println("hellowworld");
7 }

Using ArrayList instead of LinkedList

It’s easy to default to using ArrayLists, since they’re more common and familiar. However, LinkedLists perform significantly better than ArrayLists in many use cases.

In general, ArrayLists are better suited when the application demands storing and access of data. LinkedLists are better when the application demands manipulation of the stored data.

ArrayList

  • Uses a dynamic array to store elements.

  • Takes more time to manipulate the ArrayList due to the internal implementation of shifting memory bits.

  • Implements a List interface and therefore acts as a list.

LinkedList

  • Uses a doubly linked list to store elements.

  • Takes less time to manipulate a LinkedList compared to an ArrayList, since in a doubly linked list, there are no shifting memory bits.

  • Implements both the List interface and the Deque interface and therefore can act as both a list and deque.


Keep the learning going.

Refresh your Java skills without scrubbing through videos or documentation. Educative’s text-based courses are easy to skim and feature live coding environments - making learning quick and efficient.

Java for Programmers



Not handling null values

Most java developers have faced the NullPointerException at some point. The NullPointerException occurs when a variable is declared, but there is no object assigned to the variable before trying to use the contents of the variable.

A common mistake is in creating a catch phrase for NullPointerException, rather than writing code to handle underlying null pointer dereferences. Programs that catch or suppress

NullPointerExceptions, instead of handling null pointer dereferences, can make it extremely difficult to determine which expression is causing the exception in the try block or cause unnecessary performance overhead.

While there are many ways to handle null values, here are two solutions when working with a String argument.

  • Check the String argument for null
boolean isName(String s){
if (s == null) {
retrun false;
}
String names[] = s.split(" ");
if (names.length != 2) {
return false;
}
return (isCapitalized(names[0]) && is Capitalized(names[1]));
}
  • Instead of an explicit check, purposefully throw the NullPointerException
boolean isName(String s) /* Throws NullPointerException */ {
String names[] = s.split(" ");
if (names.length !=2) {
return false;
}
return (isCapitalized(names[0]) && isCapitalized(names[1]));
}

Forgetting to free resources

Forgetting to free resources results in memory occupation and collection of unused objects from network connections and file streams.

You might be running into a memory leak problem if your Java program starts to slow down or there’s noticeable degradation in performance with a large number of files as compared to a small number of files.

To avoid making this mistake, use the try-with-resources statement to implement the Closeable or AutoClosable interface to ensure closing of resources after use.

As a catch all, remind yourself to implement this statement whenever opening a file or resource connection with your program.


Ignoring method visibility

When creating methods, pay close attention to which ones you make public versus private. Public methods in an application are the visible API, so these should be as compact and readable as possible.

When methods that should be private are declared public, the internal intricacies and implementation details are revealed to the public. Conversely, when methods that should be public are declared private, you risk providing gaps in the API and running necessary unit tests.


Accessing non-static variables from static methods

Recall that a static variable requires a class to call variables and methods, while a non-static variable requires an object or instance to call variables and methods. Think of the analogy of a square being a rectangle, yet a rectangle not being a square.

Similarly, a static variable can be accessed by both static and non-static member functions, while a non-static variable cannot be accessed by a static member function.

To access a non-static variable, create an object with its own instance value.

class A {
int num1;
static int num2;
public void show() {
num2 = 10;
}
}
public class b
{
public static void main(String args [])
}
A.num2 = 5;
A obj1 = newA();
obj1.num1 = 2;

Removing an element from an array inside a loop

When iterating over an array, one common mistake is deleting or removing an element in the loop. This will cause the length of the array to shrink and shift the index improperly. Consequently, you’ll see the ConcurrentModificationException error.

To avoid this exception, use Iterator the next time you want to remove an element in a loop. Below is an example of properly removing an element from an array inside a loop.

ArrayList<String. l = new ArrayLists<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = l.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals("c")) {
iter.remove();
}
}

Inefficient string concatenation

When constructing a large String, inefficient string concatenation can result in excessive use of memory and performance deterioration.

While there are multiple ways to concatenate strings, a basic rule of thumb is to avoid using String.format() when possible as it reduces readability when working with more than two variables. Instead, optimize your code by using StringBuilder.

StringBuilder sb = new StringBuilder();
for(int i = 0; i < 1,000; i++) {
sb.append(i);
}
String final = sb.toString();

Ignoring exceptions

A common mistake for Java beginners is not writing code for handling exceptions. At first glance, it might seem okay if the code is running without any exceptions, but it can cause the code to fall silent should an exception occur.

To avoid making this mistake, make sure to properly handle your exceptions with Try-Catch-Finally, which consists of three statements:

  • Try: encloses the code block that might contain an exception

  • Catch: one or more blocks that handle the exception

  • Finally: the block that gets executed after the try block is executed successfully or a thrown exception is handled.

Below is an example for handling exceptions for a file input stream with Try-Catch-Finally.

FileInputStream input = null;
try{
File file = new File("./index.txt");
input = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
finally {
if (input !=null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}

What to learn next

Now that you’ve covered the top ten mistakes Java developers make, it’s time to refresh your Java knowledge with some hands-on practice. That way, you can apply the skills you learned here today.

No matter your skill level with Java, the following concepts are great to refresh:

  • Object oriented programming with Java
  • Java lambda expression
  • Java 8 features
  • Multi-threading basics

To get hands-on with these concepts and more, check out Educative’s learning path Java for Programmers. This path is perfect for you if you already have experience programming but need a Java refresher. You’ll cover everything from data types to Stream API, all with in-browser coding exercises.

Happy learning!


WRITTEN BYJoshua Ahn

Join a community of more than 1.4 million readers. A free, bi-monthly email with a roundup of Educative's top articles and coding tips.