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.
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.
=
, ==
, 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 }
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.
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.
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.
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])); }
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 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.
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.
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;
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(); } }
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();
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(); } } } } } }
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:
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!
Join a community of more than 1.3 million readers. A free, bi-monthly email with a roundup of Educative's top articles and coding tips.