NO. 1: Neglecting Existing Libraries
It's unquestionably a mix up for Java Developers to slight the endless measure of libraries written in Java. Just before reexamining the wheel, attempt to chase for offered libraries – a few of them have been cleaned during the time of their reality and are thoroughly allowed to utilize. These could be logging accumulations, as logback and Log4j, or system applicable accumulations, as Netty or Akka. A couple of the libraries, for example, Joda-Time, have really come to be an accepted necessity.
NO. 2: Missing the ‘break’ Keyword in a Switch-Case Block
Fall through behavior in switch statements is often useful; however,
missing a “break” keyword when such behavior is not desired can lead to
disastrous results. If you have forgotten to put a “break” in “case 0” in the
code example below, the program will write “Zero” followed by “One”, since the
control flow inside here will go through the entire “switch” statement until it
reaches a “break”. For example:
public
static void switchCasePrimer() {
int
caseIndex = 0;
switch
(caseIndex) {
case
0:
System.out.println("Zero");
case
1:
System.out.println("One");
break;
case
2:
System.out.println("Two");
break;
default:
System.out.println("Default");
}
}
In
most cases, the cleaner solution would be to use polymorphism and move code
with specific behaviors into separate classes. Java mistakes such as this one
can be detected using static code analyzers, e.g. FindBugsand PMD.
NO. 3: Forgetting to Free Resources
This mistake may lead to resources leaked or memory occupied by no
longer used objects.
public static void copyFile(File sourceFile, File destFile) {
FileChannel sourceChannel = null;
FileChannel destChannel = null;
try {
sourceChannel
= new FileInputStream(sourceFile).getChannel();
destChannel = new FileOutputStream(destFile).getChannel();
sourceChannel.transferTo(0,
sourceChannel.size(), destChannel);
} catch (IOException ex) {
ex.printStackTrace();
}
}
A solution to this is using the try-with-resources structure
available since Java 7, which automatically closes the resources. example, the above code can be re-written like the following code:
public static void copyFile(File sourceFile, File destFile) {
try (
FileChannel
sourceChannel = new FileInputStream(sourceFile).getChannel();
FileChannel destChannel
= new FileOutputStream(destFile).getChannel();
) {
sourceChannel.transferTo(0,
sourceChannel.size(), destChannel);
} catch (IOException ex) {
ex.printStackTrace();
}
}
NO. 4: Memory Leaks
Memory leaks in Java can
happen in various ways, but the most common reason is everlasting object
references, because the garbage collector can’t remove objects from the heap
while there are still references to them. One can create such a reference by
defining class with a static field containing some collection of objects, and
forgetting to set that static field to null after the collection is no longer
needed. Static fields are considered GC roots and are never collected.
Another potential explanation for such memory spills is a gathering of items referencing each other, causing round conditions so the junk jockey can't choose whether these articles with cross-reliance references are required or not. Another issue is spills in non-load memory when JNI is utilized.
NO. 5: Excessive Garbage Allocation
Excessive
garbage allocation may happen when the program makes a ton of fleeting items. The junk jockey works consistently, expelling unneeded articles from memory, which impacts applications' execution adversely.
An Example:
String
oneMillionHello = "";
for
(int i = 0; i < 1000000; i++) {
oneMillionHello = oneMillionHello + "Hello!";
}
System.out.println(oneMillionHello.substring(0,
6));
In
Java, strings are immutable. So, on each iteration a new string is created. To
address this we should use a mutable StringBuilder:
StringBuilder
oneMillionHelloSB = new StringBuilder();
for (int i = 0; i < 1000000; i++) {
oneMillionHelloSB.append("Hello!");
}
System.out.println(oneMillionHelloSB.toString().substring(0,
6));
While the principal rendition requires a considerable amount of time to execute, the form that utilizations StringBuilder produces an outcome in an essentially less measure of time.
NO. 6: Using Null References without Need
Maintaining a strategic distance from inordinate utilization of invalid is a decent practice. For instance, it's desirable over return discharge exhibits or accumulations from techniques rather than nulls, since it can help avert NullPointerException.
Consider the accompanying technique that navigates an accumulation acquired from another strategy, as demonstrated as follows:
List<String>
accountIds = person.getAccountIds();
for
(String accountId : accountIds) {
processAccount(accountId);
}
If
getAccountIds() returns null when a person has no account, then
NullPointerException will be raised.To settle this, an invalid check will be required. Be that as it may, if rather than an invalid it restores a void rundown, at that point NullPointerException is not any more an issue. Besides, the code is cleaner since we don't have to invalid check the variable accountIds.
To manage different situations when one needs to stay away from nulls, distinctive techniques might be utilized. One of these techniques is to utilize Optional sort that can either be a void protest or a wrap of some esteem:
Optional<String>
optionalString = Optional.ofNullable(nullableString);
if(optionalString.isPresent())
{
System.out.println(optionalString.get());
}
In
fact, Java 8 provides a more concise solution:
Optional<String>
optionalString = Optional.ofNullable(nullableString);
optionalString.ifPresent(System.out::println);
NO. 7: Ignoring Exceptions
In This case the exceptions occurred, the code can fail silently
which adds difficulty in finding the problem.
look at the following program:
public class Sum {
public static void main(String[] args) {
int a = 0;
int b = 0;
try {
a
= Integer.parseInt(args[0]);
b
= Integer.parseInt(args[1]);
} catch (NumberFormatException
ex) {
}
int sum = a + b;
System.out.println("Sum
= " + sum);
}
}
program calculates the sum of two numbers passed via command-line
arguments. Note that the catch block is left empty. If we try to run this
program by the following command line:
java Sum 123 456y
will fail silently:
Sum = 123
It’s because the second argument 456y causes a NumberFormatException to
be thrown, but there’s no handling code in the catch block so the program
continues with incorrect result.
So to avoid such potential problem, always handle exceptions at least
by printing the stack trace to inform the error when it happens:
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {
ex.printStackTrace();
}
It save your hours of debugging later if the problem occurs.
NO. 8:
Modifying a collection while iterating it
This exemption happens when a gathering is changed while repeating over it utilizing techniques other than those gave by the iterator protest. For instance, we have a rundown of caps and we need to evacuate every one of those that have ear folds:
List<IHat>
hats = new ArrayList<>();
hats.add(new
Ushanka()); // that one has ear flaps
hats.add(new
Fedora());
hats.add(new
Sombrero());
for
(IHat hat : hats) {
if (hat.hasEarFlaps()) {
hats.remove(hat);
}
}
If
you run this code, “ConcurrentModificationException” will be raised since the
code modifies the collection while iterating it. The same exception may occur
if one of the multiple threads working with the same list is trying to modify
the collection while others iterate over it. Simultaneous change of accumulations in numerous strings is a characteristic thing, yet ought to be treated with regular apparatuses from the simultaneous programming tool kit, for example, synchronization locks, exceptional accumulations embraced for simultaneous adjustment, and so forth. There are inconspicuous contrasts to how this Java issue can be settled in single strung cases and multithreaded cases. The following is a concise discourse of some ways this can be dealt with in a solitary strung situation:
Gather questions and evacuate them in another circle
Gathering caps with ear folds in a rundown to expel them later from inside another circle is a conspicuous arrangement, however requires an extra accumulation for putting away the caps to be expelled:
List<IHat>
hatsToRemove = new LinkedList<>();
for
(IHat hat : hats) {
if (hat.hasEarFlaps()) {
hatsToRemove.add(hat);
}
}
for
(IHat hat : hatsToRemove) {
hats.remove(hat);
}
Use
Iterator.remove method
This
approach is more concise, and it doesn’t need an additional collection to be
created:
Iterator<IHat>
hatIterator = hats.iterator();
while
(hatIterator.hasNext()) {
IHat hat = hatIterator.next();
if (hat.hasEarFlaps()) {
hatIterator.remove();
}
}
Utilizing List Iterator’s methods
Utilizing the listiterator is suitable when the adjusted gathering actualizes List interface. Iterators that execute List Iterator interface bolster evacuation operations, as well as include and set operations. ListIterator actualizes the Iterator interface so the illustration would look practically the same as the Iterator evacuate technique. The main distinction is the kind of cap iterator, and the way we acquirethat iterator with the “listIterator()” method. The snippet
below shows how to replace each hat with ear flaps with sombreros using
“ListIterator.remove” and “ListIterator.add” methods:
IHat
sombrero = new Sombrero();
ListIterator<IHat>
hatIterator = hats.listIterator();
while
(hatIterator.hasNext()) {
IHat hat = hatIterator.next();
if (hat.hasEarFlaps()) {
hatIterator.remove();
hatIterator.add(sombrero);
}
}
With
ListIterator, the remove and add method calls can be replaced with a single
call to set:
IHat
sombrero = new Sombrero();
ListIterator<IHat>
hatIterator = hats.listIterator();
while
(hatIterator.hasNext()) {
IHat hat = hatIterator.next();
if (hat.hasEarFlaps()) {
hatIterator.set(sombrero); // set
instead of remove and add
}
}
Utilize stream strategies presented in Java 8 With Java 8, software engineers can change a gathering into a stream and channel that stream as indicated by a few criteria. Here is a case of how stream programming interface could enable us to channel caps and evade
“ConcurrentModificationException”.
hats
= hats.stream().filter((hat -> !hat.hasEarFlaps()))
.collect(Collectors.toCollection(ArrayList::new));
The
“Collectors.toCollection” technique will make another ArrayList with separated caps. This can be an issue if the separating condition were to be fulfilled by an expansive number of things, bringing about a huge ArrayList; subsequently, it ought to be use with mind. Utilize List.removeIf strategy displayed in Java 8 Another arrangement accessible in Java 8, and plainly the most succinct, is the utilization of the “removeIf”
method:
hats.removeIf(IHat::hasEarFlaps);
That’s
it. Under the hood, it uses “Iterator.remove” to accomplish the behavior.
Utilize specialized collections
On the off chance that at the earliest reference point we chose to utilize "CopyOnWriteArrayList" rather than "ArrayList", at that point there would have been no issue by any means, since "CopyOnWriteArrayList" gives adjustment techniques, (for example, set, include, and evacuate) that don't change the support exhibit of the gathering, but instead make another altered form of it. This permits emphasis over the first form of the gathering and alterations on it in the meantime, without the danger of "ConcurrentModificationException". The downside of that gathering is self-evident - era of another accumulation with every adjustment.
There
are other collections tuned for different cases, e.g. “CopyOnWriteSet” and
“ConcurrentHashMap”.
Another
possible mistake with concurrent collection modifications is to create a stream
from a collection, and during the stream iteration, modify the backing
collection. The general rule for streams is to avoid modification of the
underlying collection during stream querying. The following example will show
an incorrect way of handling a stream:
List<IHat>
filteredHats = hats.stream().peek(hat -> {
if (hat.hasEarFlaps()) {
hats.remove(hat);
}
}).collect(Collectors.toCollection(ArrayList::new));
The strategy look accumulates every one of the components and plays out the gave activity on every one of them. Here, the activity is endeavoring to expel components from the basic rundown, which is incorrect. To maintain a strategic distance from this, attempt a portion of the strategies portrayed previously.
NO. 9: Breaking Contracts
Sometimes,
code that is provided by the standard library or by a third-party vendor relies
on rules that should be obeyed in order to make things work. For example, it
could be hashCode and equals contract that when followed, makes working
guaranteed for a set of collections from the Java collection framework, and for
other classes that use hashCode and equals methods. Disobeying contracts isn’t
the kind of error that always leads to exceptions or breaks code compilation;
it’s more tricky, because sometimes it changes application behavior without any
sign of danger. Erroneous code could slip into production release and cause a
whole bunch of undesired effects. This can include bad UI behavior, wrong data
reports, poor application performance, data loss, and more. Fortunately, these
disastrous bugs don’t happen very often. I already mentioned the hashCode and
equals contract. It is used in collections that rely on hashing and comparing
objects, like HashMap and HashSet. Simply put, the contract contains two rules:
If
two objects are equal, then their hash codes should be equal.
If
two objects have the same hash code, then they may or may not be equal.
Breaking
the contract’s first rule leads to problems while attempting to retrieve
objects from a hashmap. The second rule signifies that objects with the same
hash code aren’t necessarily equal. Let us examine the effects of breaking the
first rule:
public
static class Boat {
private String name;
Boat(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() !=
o.getClass()) return false;
Boat boat = (Boat) o;
return !(name != null ?
!name.equals(boat.name) : boat.name != null);
}
@Override
public int hashCode() {
return (int) (Math.random() * 5000);
}
}
As
you can see, class Boat has overridden equals and hashCode methods. However, it
has broken the contract, because hashCode returns random values for the same
object every time it’s called. The following code will most likely not find a
boat named “Enterprise” in the hashset, despite the fact that we added that
kind of boat earlier:
public
static void main(String[] args) {
Set<Boat> boats = new
HashSet<>();
boats.add(new Boat("Enterprise"));
System.out.printf("We have a boat
named 'Enterprise' : %b\n", boats.contains(new Boat("Enterprise")));
}
Another
example of contract involves the finalize method. Here is a quote from the
official java documentation describing its function:
”The
general contract of finalize is that it is invoked if and when the JavaTM
virtual machine has determined that there is no longer any means by which this
object can be accessed by any thread (that has not yet died), except as a
result of an action taken by the finalization of some other object or class
which is ready to be finalized. The finalize method may take any action,
including making this object available again to other threads; the usual
purpose of finalize, however, is to perform cleanup actions before the object
is irrevocably discarded. For example, the finalize method for an object that
represents an input/output connection might perform explicit I/O transactions
to break the connection before the object is permanently discarded.“
One
could decide to use the finalize method for freeing resources like file
handlers, but that would be a bad idea. This is because there’s no time
guarantees on when finalize will be invoked, since it’s invoked during the
garbage collection, and GC’s time is indeterminable.
NO. 10: RAW TYPE USAGE
Raw types, according to Java specifications, are types that are either
not parametrized, or non-static members of class R that are not inherited from
the superclass or superinterface of R. There were no alternatives to raw types
until universal types were presented in Java. It sustains generic programming
considering that version 1.5, and generics were unquestionably a significant
improvement. Nonetheless, due to backwards compatibility factors, a mistake has
been left that can potentially break the type system
.