The Top 11 Most Common Mistakes Made by Java Developers Concerning Buggy Java Code — Come On Don’t Do That!

  1. Neglecting Existing Libraries

It’s unquestionably a mistake for Java developers to disregard the countless Java libraries that exist. Search for available libraries before creating something from scratch; many of them have been refined over time and are free to use. These could be network-related libraries like Netty or Akka, or logging libraries like logback and Log4j. Some of the libraries, like Joda-Time, are now accepted as being the norm.

The next personal experience comes from one of my earlier initiatives. The portion of the program that handles HTML escaping was entirely new. Years of successful operation were followed by a user input that sent the program into an endless loop. The user tried using the same input again after discovering that the service was unresponsive. This infinite loop eventually took every CPU on the server designated for this application.

2. Instead of a parameterized type, use raw type

Raw types are types that are either not parametrized or non-static elements of class R that are not inherited from the superclass or superinterface of R, as defined by the Java standards. Before Java’s inclusion of generic types, raw types had no substitutes. Since version 1.5, it has supported generic programming, which unquestionably represented a huge advancement. The type system has, however, been left open to a pitfall due to backward compatibility concerns. Let’s consider the following illustration:

List listOfNumbers = new ArrayList();
listOfNumbers.add(10);
listOfNumbers.add("Ten");
listOfNumbers.forEach(n -> System.out.println((int) n * 2));

Here, we have a raw ArrayList that is specified as a list of numbers. We can add any object into it because its type isn’t determined by the type parameter. The last line, however, casts the elements to int, doubles the value, and prints the result to standard output. Although this code will compile without issues, it will throw a runtime exception when executed since we tried to cast a string to an integer. It should go without saying that if we withhold crucial information from the type system, it cannot assist us in writing safe code. To solve the issue, we must describe the kind of objects that will be kept in the collection:

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

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

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

The line defining the collection is the only thing that differs from the original:

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

Because we are attempting to add a string to a collection that is designed to only hold numbers, the fixed code would not compile. When we attempt to add the string “Twenty” to the list, the compiler will indicate an error and highlight the line in question. Generic types should always be parametrized. This allows the compiler to do all feasible type checks and reduces the likelihood of runtime exceptions brought on by type system incompatibilities.

3.Concurrent Modification Exception

This error happens when a collection is altered while being iterated over using a method other than the iterator object’s built-in methods. As an illustration, suppose we have a list of hats and want to cross out any that have ear flaps:

List<ILinux> linux = new ArrayList<>();
hats.add(new Ubuntu());
hats.add(new Fedora());
hats.add(new Redhat());
for (IHat hat : hats) {
if (linux.hasGNOME()) {
linux.remove(hat);
}
}

This function alters the collection while iterating through it, therefore if we run it, “ConcurrentModificationException” will be thrown. If one of the several threads working with the same list tries to modify the collection while others iterate over it, the same exception can occur. Multiple threads modifying a collection at the same time is common, however it should be handled with the standard concurrent programming tools like synchronization locks, special collections designed for concurrent modification, etc. The way this Java problem can be fixed in single-threaded circumstances and multi-threaded cases differs just slightly. A quick description of a few approaches to handling this in a single threaded context is provided below:

Gather objects and eliminate them in a subsequent loop.

The apparent answer is to keep hats with ear flaps in a list to remove them from later from within another loop, but this requires a separate collection for holding the hats to be removed:

List<ILinux> hatsToRemove = new LinkedList<>();
for (ILinux l : linux) {
if (l.hasGNOME()) {
hatsToRemove.add(l);
}
}
for (ILinux l : hatsToRemove) {
linux.remove(l);
}

4. Ignoring exceptions will be a problem in the future

It can be tempting to not handle exceptions. However, handling them is the ideal approach for both inexperienced and seasoned Java developers. Since exceptions are typically thrown on purpose, we must usually deal with the problems that led to them. Do not ignore these occurrences. You have the option to rethrow it, display an error prompt to the user, or add a message to the log as necessary. To at least inform other developers of the rationale, it should be stated why the exception was not handled.

linux = dist.getLinux();
try {
linux.show();
} catch (NullPointerException e) {
// Maybe no linux dist here ?!?!?
}

Encoding this message into the exception’s variable name, like in the following example, will make it more obvious how unimportant an error is:

try { linux.delete(); } catch (NullPointerException unimportant) {  }

5. Repeating Garbage Allocation

The creation of several transient objects by the software may result in excessive garbage allocation. The garbage collector regularly purges unnecessary items from memory, which adversely affects the performance of applications. One straightforward case:

String oneMillionTerminal = "";
for (int i = 0; i < 1000000; i++) {
oneMillionTerminal = oneMillionTerminal + "Terminal";
}
System.out.println(oneMillionTerminal.substring(0, 6));

Strings in Java programming are immutable. Thus, a new string is generated after each repetition. We should employ a changeable StringBuilder to address this:

StringBuilder oneMillionTerminalSB = new StringBuilder();
for (int i = 0; i < 1000000; i++) {
oneMillionTerminalSB.append("Terminal");
}
System.out.println(oneMillionTerminalSB.toString().substring(0, 6));

While the first version takes quite some time to complete, the version that makes use of StringBuilder completes the task in a lot less time.

6. Confusing =, ==, and .equals() Which was ?

It is simple to mistakenly mix together these three operators, especially the last two. A referential equality comparison, or ==, determines if two objects point to the same memory address. Keep in mind that = is used for assignment. The equals() function determines whether the values of two objects are equal.

Always use.equals() when comparing objects, especially strings, as a general rule.

String a = "hi";
String b = "linux";

if ( (a+b).equals("hilinux") )
{
System.out.println("hilinux");
}

7. Using ArrayList instead of LinkedList

ArrayLists are a simple default choice since they are more widely used and understood. But in many situations, LinkedLists outperform ArrayLists dramatically.

ArrayLists are generally more appropriate when the application requires storing and accessing data. When the application necessitates manipulating the stored data, LinkedLists are preferable.

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.

8. Who doesn’t matter null values ?

The NullPointerException has been a problem for most Java developers at some point. When a variable is declared but no object is assigned to it before attempting to use the contents of the variable, the NullPointerException is thrown.

Making a catchphrase for NullPointerException rather than developing code to deal with underlying null pointer dereferences is a common error. programs that detect or block

It can be very challenging to identify which expression in the try block is producing the exception or can result in extra performance cost when NullPointerExceptions are used in place of handling null pointer dereferences.

Although there are other approaches to dealing with null values, there are two that work well when dealing with a String argument.

9. Is method visibility important ?

Keep a close eye on the techniques you designate as public versus private. Public methods in an application should be as concise and readable as feasible because they represent the visible API.

The internal workings and implementation details of methods that ought to be secret are made public when they are made public. On the other hand, when methods that ought to be public are set to private, you run the danger of leaving API gaps and failing to execute the required unit tests. :)

10. Passwords as String (Hackers Comingggggg!!!!)

Passwords kept in String objects provide a security risk since they can be stolen through memory assaults.

As JPasswordField and Password4j already do, you ought to use char[]. There isn’t much you can do in this situation if we’re talking about online apps because the majority of web containers pass the plain text password in the HttpServletRequest object as a String.

11. Memory Leaks — Need Help Houston!!!!!

Although it is comforting to not have to worry about manually allocating and clearing memory when using Java, a novice Java developer should still be aware of how memory is used in the program. Memory allocation issues could still arise. A program won’t be freed as long as it creates references to objects that are no longer required. We can still refer to this as a memory leak in a sense. Everlasting object references are the most frequent cause of memory leaks in Java because the garbage collector cannot remove objects from the heap while there are references to them. Such a reference can be created by including a static field in a class definition that contains a collection of objects and then neglecting to set that static field to null after the collection is no longer required. Static fields are never collected since they are regarded as GC roots.

A set of objects referencing one another might also result in circular dependencies, which makes it difficult for the garbage collector to determine whether or not these objects with cross-dependency references are necessary. When JNI is utilized, non-heap memory leaks are another problem.

The following is an example of a basic leak:

#Such a good control :)final ScheduledRepeaterService scheduledRepeaterService = Repeaters.newScheduledThreadPool(1);
final Deque<BigDecimal> numbers = new LinkedBlockingDeque<>();
final BigDecimal divisor = new BigDecimal(51);

scheduledRepeaterService.scheduleAtFixedRate(() -> {
BigDecimal number = numbers.peekLast();
if (number != null && number.remainder(divisor).byteValue() == 0) {
System.out.println("Number Is: " + number);
System.out.println("Deque size is: " + numbers.size());
}
}, 10, 10, TimeUnit.MILLISECONDS);

scheduledRepeaterService.scheduleAtFixedRate(() -> {
numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);

try {
scheduledRepeaterService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}

Thanks For Reading!

--

--