Top 10 mistakes that java developers should avoid

Back Top 10 mistakes that java developers should avoid

Java is a fast, secure and reliable object oriented programming language. Java has one of the largest pool of software developers worldwide. With this large developer base, mistakes are natural. This post will focus on the top 10 common mistakes java developer should avoid-

  1. Collection modifying during iteration

If an underlying collection is modified during iteration, ConcurrentModificationException is thrown, thereby causing the iteration to stop. This is also known as fail-fast nature of the Collection iterator. Following are some of the approaches to handle this in a single threaded scenario-

Using Iterator or ListIterator –

Iterator<MyObject> myObjIterator = myObjArrayList.iterator();

while (myObjIterator.hasNext()){

MyObject obj = myObjIterator.next();

if (obj.removeConditionFullfilled()){

myObjIterator.remove();

}

}  

In case of list you can also use ListIterator. Difference between Iterator and ListIterator is a topic of another post.

  1. Comparing objects with == instead of .equals()

This is a common error among the less experienced java developers. Comparing objects with == is actually comparing the two references, if they point to the same object. We must instead use .equals() method, which is inherited  by all classes from java.lang.Object.

  1. Breaking hash code and equals contract

Let us consider following example-

import java.util.HashMap;

public class Book {

private String name;

public Book(String name) {

this.name = name;

}

public boolean equals(Object obj) {

if(obj==null) return false;

if (!(obj instanceof Book))

return false;

if (obj == this)

return true;

return this.name.equals(((Book) obj).name);

}

public static void main(String[] args) {

Book a1 = new Book ("java");

Book a2 = new Book ("ruby");

//hashMap stores Book type and its quantity

HashMap<Book, Integer> m = new HashMap< Book, Integer>();

m.put(a1, 10);

m.put(a2, 20);

System.out.println(m.get(new Book ("java")));

}

}

In this example, a java book object is stored successfully in a HashMap. But, when you ask the map to get this object, the object is not found. This will print null.

This problem is caused by on-overridden hashCode method.

To revisit, the contract between equals() and hashCode() is-

  1. If two objects are equal, then they must have the same hash code.
  2. If two objects have the same hash code, they may or may not be equal.

This contract must be made in all scenarios. If equals() method is overridden, hashCode() must also be overridden to maintain the contract.

  1. Memory leaks

Though java manages memory automatically, it is still possible to face problems related to memory allocation. Following may still cause memory leak in your java application-

  1. Keeping reference to objects that are no longer required.
  2. Static fields and collections causing memory leak as they will live as long as the program live.

  3. In a multi-threaded application, every thread instance has its own instance of states bound to it. However, this can be dangerous because thread local will not be removed by garbage collection as long as the thread is alive.

  1. Forgetting to free resource

When dealing with a resource in java like- files, network connections, database, it is important to free the resource when not required. Resources that are not freed will remain in memory and will cause memory leaks. When using resource in try-catch block, it is always advisable to add clean-up code in the finally block to ensure its execution even in case of unexpected exceptions.

In Java 7 and further, you can also use try-with-resource.

  1. Using String to store sensitive data like passwords

Strings are immutable and stored in String Pool in heap. If another process can dump memory, there is a chance of losing sensitive data like passwords.   

  1. Returning null from API methods

While there are some cases when returning null from methods is ok, but there cannot be any suitable use case of returning null collections. It will only increase risk of null pointer exceptions. Returning empty collection is always more suitable.

  1. Using Wrapper classes where primitives are also as effective

Though wrapper classes are effective, they are slower than primitives. Primitives are just values, whereas wrapper classes stores information about complete class.

  1. Using raw type collections

Using raw type collection is dangerous as the raw type collections skip the generic type checking and not safe. Consider the following code as example-

public static void add(List list, Object o){

list.add(o);

}

public static void main(String[] args){

List<String> list = new ArrayList<String>();

add(list, 10);

String s = list.get(0);

}

This code will throw an exception:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at ...

  1. Declaring constructor as a method

Constructors and methods look a lot alike. The only obvious syntactical difference is that the constructors does not have any return type. Java also permits methods (like constructors) to have same name as class. Consider following case-

public class DummyClass {

   List list;

   // This may look like a constructor, but it is a method

   public void DummyClass() {

       list = new ArrayList<Integer>();

   }

   public append(int n) {

       list.addElement(new Integer(n));

   }

}

This code will throw a null pointer error the first time append method is called. It may be expected that the list should be initialized within the default constructor. However, it is not a constructor but a method.