10 Common Mistakes that Java Programmers Make

10 Common Mistakes that Java Programmers Make

Java is a simple and powerful object oriented programming language and it is in many respects similar to C++. Java programs are platform independent which means they can be run on any operating system with any type of processor as long as the Java interpreter is available on that system. Whether you program regularly in java, or whether you’re new to the language, you’ll make mistakes. Here are the list  of 10 common  java programming mistakes that developers frequently make.

1. Accessing Non-Static Member Variables from Static Methods

Many programmers, especially when initially familiarize with Java, have issues with accessing member variables from their main method. The method signature for main is marked static – meaning that we don’t need to create an instance of the class to invoke the main method.

For example, Java Virtual Machine (JVM) could call the class MyApplication like this :

MyApplication.main ( command_line_args );

This means, however, that there is not an instance of MyApplication –  it doesn’t have any member variables to access.

For example,  Below application will generate a compiler error message.

public class StaticDemo {
        public String my_member_variable = "somedata"; 
        public static void main (String args[]) {          
       // Access a non-static member from static method             
       System.out.println ("This generates a compiler error" + my_member_variable );    
  } }

 

If you want to access its member variables from a non-static method, you must create an instance of the object. See the below example :

public class NonStaticDemo { 
        public String my_member_variable = "somedata...";        
        public static void main (String args[])   {                
        NonStaticDemo demo = new NonStaticDemo();             
        // Access member variable of demo             
        System.out.println ("This won't generate an error" + demo.my_member_variable );  
 } }

 

2. Java Programming Mistakes while Using Raw Type Instead of a Parameterized

According to Java specifications, Raw types are either not parameterized, or non-static members of Class R [i.e.  Not inherited from the super class or super interface of R ]. There were no alternatives to raw types until generic types were introduced in Java. However, due to backward compatibility reasons, a pitfall has been left that could potentially break the type system.

List listOfNumbers = new ArrayList();

listOfNumbers.add(5);
listOfNumbers.add("Ten");

listOfNumbers.forEach(n -> System.out.println((int) n * 2));

Here we have a list of numbers defined as a raw ArrayList. Since its type isn’t specified with type parameter, we can add any object into it. Yet, in the last line we cast elements to int, double it, and print the doubled number to standard output. This code will compile without errors, but once running it will raise a runtime exception because we attempted to cast a string to an integer. Obviously, the type system is unable to help us write safe code if we hide necessary information from it. To fix this problem we have to specify the type of objects that will store in the collection:

List<Integer> listOfNumbers = new ArrayList<>();

listOfNumbers.add(5);
listOfNumbers.add("Ten");

listOfNumbers.forEach(n -> System.out.println((int) n * 2));

The fixed code wouldn’t compile because we are trying to add a string into a collection that is expected to store integers only. The compiler will show an error and point at the line where we are trying to add the string “Ten” to the list. It’s always a good idea to parameterize generic types. That way, the compiler is able to make all possible type checks, and the chances of runtime exceptions caused by type system inconsistencies are minimized.

3. Constructor of Super and Sub

class Super {
          String s;
          Public Super (String s) {
                this.s = s;
          }
}

public class Sub extends Super {
          int x = 100;
          public Sub(String s) {
          }
          public sub() {
           System.out.println("sub");
          }
          public static void main(String[] args) {
           Sub s = new Sub ();
         }
}

This compilation error happens because the default super constructor is undefined. In Java, if a class does not define a constructor, compiler will insert a default no-argument constructor for the class by default.  If a constructor is defined in Super class, compiler will not insert the default no-argument constructor. This is the situation for the above Super Class.

The constructors of the Sub class, either with-argument or no-argument, will call the no-argument Super constructor. Since compiler tries to insert super() to the two constructors in the Sub class, but the Super’s default constructor is not defined, compiler reports the error message.

To fix this problem, Simply

Add a Super() constructor to the Super class like

public Super() {
    System.out.println("Super");
}

(or ) Remove the self-defined Super constructor

(or ) Add super(value) to sub constructors.

4. Excessive Garbage Allocation

Excessive garbage allocation may happen when the program creates a lot of short-term objects. The garbage collector works continuously, removing unneeded objects from memory, which impacts application’s performance in a negative way. Below is the Sample example :

String oneLakhHello = " ";
for (int i = 0; i < 100000; i++) {
    oneLakhHello = oneLakhHello + "Hello!";
}
System.out.println(oneLakhHello.substring(0, 4));

In Java, strings are immutable. So, on each iteration a new string is created. To address this we should use a mutable StringBuilder:

StringBuilder oneLakhHelloSB = new StringBuilder();
    for (int i = 0; i < 100000; i++) {
        oneLakhHelloSB.append("Hello!");
    }
System.out.println(oneLakhHelloSB.toString().substring(0, 4));

While the first example requires quite a bit of time to execute, the next example that uses StringBuilder produces a result in a significantly less amount of time.

5. Convert Array to ArrayList

To convert an array to an ArrayList, most of the developers frequently do this:

List<String> list = Arrays.asList(arr);

Arrays.asList() will return an ArrayList which is a private static class inside Arrays, it is not the java.util.ArrayList class. The java.util.Arrays.ArrayList class has set(), get(), contains() methods, but does not have any methods for adding elements, so its size is fixed.

To create a real ArrayList, you should do:

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

The constructor of ArrayList can accept a Collection type, which is also a super type for java.util.Arrays.ArrayList.

6. Remove an Element from a List Inside a Loop

See the below code which removes elements during iteration:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("w", "x", "y", "z"));
for (int i = 0; i < list.size(); i++) {
            list.remove(i);
}
System.out.println(list);      // Output : [x, z]

There is a drawback during this method. When an element is removed, the size of the list shrinks and the index changes. So if you want to delete multiple elements inside a loop by using the index, that will not work properly.

You may know that utilizing iterator is the correct approach to delete elements inside loops, and you know foreach loop in Java works like an iterator, but actually it is not.

See the below example, which will throw out Concurrent Modification Exception :

ArrayList<String> list = new ArrayList<String>(Arrays.asList("w", "x", "y", "z"));
for (String s : list) {
            if (s.equals("x"))
                        list.remove(s);
}

Instead of the above the following is  OK :

ArrayList<String> list = new ArrayList<String>(Arrays.asList("w", "x", "y", "z"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
            String s = iter.next();
            if (s.equals("w")) {
                        iter.remove();
            }
}

next() must be called before .remove(). In the foreach loop, compiler will make the .next() called after the operation of removing element, which caused the Concurrent Modification Exception.

7. Comparing two objects ( == instead of .equals)

When we use the == operator, we are actually comparing two object references to check whether they point to the same object. We cannot compare two strings for equality, using the == operator. We should rather utilize the .equals method, which is a method inherited by all classes from java.lang.Object.

Here is the correct way to compare two strings.

String abc = "abc"; String def = "def";

// Wrong way
if ( (abc + def) == "abcdef" ) {
    ......
}

//Correct way
if ( (abc + def).equals("abcdef") )
{
   .....
}

8. Preventing concurrent access to shared variables by threads

When writing multi-threaded applications, most of the programmers usually perform and leave their applications and applets vulnerable to thread conflicts. When two or more threads access the same data concurrently, there exists the possibility that two threads will access or modify the same data at the same time. Don’t be fooled into thinking that such issues won’t happen on single-threaded processors. While accessing some data your thread may be suspended, and another thread scheduled. It writes its data, which is then overwritten when the first thread makes its changes.

Such issues are not simply restricted to multi-threaded applications or applets. If you write Java APIs, or JavaBeans, then your code may not be thread-safe. Although if you never write a single application that uses threads, people that use your code will. For the sanity of others, you should always take precautions to prevent concurrent access to shared data.

The simplest method to solve this problem is that make your variables private and use synchronized accessor methods. Accessor methods allow access to private member variables, but in a controlled manner.

See the below example, which provide a safe way to change the value of a counter.

public class MyCounter
{
            private int count = 0; // count starts at zero
            public synchronized void setCount(int amount) {
                        count = amount;
            }

            public synchronized int getCount()
            {
                       return count;
            }
   }

9. Forget to check for Preconditions, Null Pointers

Most of the bugs in Java are NullPointerExceptions. These exceptions frequently come from violations or not specified contracts between parts of the application. This results in preconditions of a function call that are not met. Here defensive programming teaches to check whenever you are in doubt excplicitly about the method arguments. This can be done either with a conditional, throwing the appropriate Exception (e.g. IllegalArgumentException) or since JDK1.4.x it can be done using the built in assert statements.

10. Not Paying Attention to Class Cloneability

The cloning mechanism available in Java can enable the hacker to create new instances of the classes you have defined. This can be performed without any constructed execution. Safe Java development is basically the turning of the objects to non-cloneable.

This can be accomplished with the help of the following piece of code

public final void clone() throws java.lang.CloneNotSupportedException {
          throws new java.lang.CloneNotSupportedException();
}

If you want to go the cloning route, it’s possible to achieve overriding immunity by using the final keyword.

public final void clone() throws java.lang.CloneNotSupportedException
{
         super.clone ();
}

Submit your Java Errors, issues or problems and get it fixed by an Expert Java Programmer