Long idNumber;
long idNumber; // long takes less memory than Long
public boolean isOdd(int num) {
return (num & 1) != 0;
}
// best way to check the oddity of a number
3) Avoid redundant initialization (0, false, null..)
Do not initialize variables with their default initialization, for example, a boolean
by default has the value false
so it is redundant to initialize it with the false value.
String name = null; // redundant
int speed = 0; // redundant
boolean isOpen = false; // redundant
String name;
int speed;
boolean isOpen;
// same values in a cleaner way
4) Declare class members private wherever possible and always give the most restricted access modifier.
public int age; // very bad
int age; // bad
private int age; // good
5) Avoid the use of the ‘new’ keyword while creating a String
String s1 = new String("AnyString") ;
// low instantiation
// The constructor creates a new object, and adds the literal to the heap
String s2 = "AnyString" ; // better
// fast instantiation
// This shortcut refers to the item in the String pool
// and creates a new object only if the literal is not in the String pool.
6) Use the concat method when concatenating possibly-empty Strings, and use + otherwise (since Java 8).
String address = streetNumber.concat(" ").concat(streetName)
.concat(" ").concat(cityName).concat(" ").concat(cityNumber)
.concat(" ").concat(countryName);
// streetNumber, streetName, cityName, cityNumber
// and countryName are user inputs and can be empty
String str1 = "a";
String str2 = "b";
String str3 = "c";
String concat = str1 + str2 + str3;
NB: Before Java 8, StringBuilder
andStringBuffer
were the best way for concatenating strings.
7) Using underscores in Numeric Literals.
int myMoneyInBank = 58356823;
int myMoneyInBank = 58_356_823; // more readable
long billsToPay = 1000000000L;
long billsToPay = 1_000_000_000L; // more readable
8) Avoid using ‘for loops’ with indexes.
Do not use a for loop with an index (or counter) variable if you can replace it with the enhanced for loop (since Java 5) or forEach
(since Java 8). It’s because the index variable is error-prone, as we may alter it incidentally in the loop’s body, or we may start the index from 1 instead of 0.
for (int i = 0; i < names.length; i++)
{ saveInDb(names[i]); }
for (String name : names)
{ saveInDb(name); } // cleaner
9) Replace try–catch-finally with try-with-resources.
Scanner scanner = null;
try
{scanner = new Scanner(new File("test.txt"));
while (scanner.hasNext())
{System.out.println(scanner.nextLine());}}
catch (FileNotFoundException e)
{e.printStackTrace();}
finally
{if (scanner != null)
{scanner.close();}}
// error-prone as we can forget to close the scanner in the finally block
try (Scanner scanner = new Scanner(new File("test.txt")))
{while (scanner.hasNext())
{System.out.println(scanner.nextLine());}}
catch (FileNotFoundException e)
{e.printStackTrace();}
// cleaner and more succinct
10) No vacant catch blocks
An empty catch block will make the program fail in silence and will not give any information about what went wrong.
try
{ productPrice = Integer.parseInt(integer); }
catch (NumberFormatException ex)
{ }
// fail silently without giving any feedback
try
{ productPrice = Integer.parseInt(integer); }
catch (NumberFormatException ex)
{unreadablePrices.add(productPrice); // handle error
log.error("Cannot read price : ", productPrice );// proper and meaningful message
}
11) Avoid the null pointer exception as possible.
Try to avoid the null pointer exceptions that may occur in runtime by:
- Return Empty Collections instead of returning Null elements
- Use
Optional
as you can requireNonNull
methods ofjava.utils.Objects
NotNull
,NotEmpty
,NotBlank
AnnotationsObjects::nonNull
inStreams
requireNonNull
methods injava.util.Objects
12) Add only needed getters, setters and constructors and avoid Lombok (YAGNI).
Add only the code that you need.
Lombok is a great tool that may help you in generating some boilerplate code but it has some drawbacks like IDE incompatibility, usage of non-public API and it is closely tied to the Java compiler. Besides that since Java 14, records can replace some of its utilities.
13) Always implement hashCode when you implement equals.
Always remember to override hashCode
if you override equals
so as not to "break the contract".
As per the API, the result returned from the hashCode()
method for two objects must be the same if their equals
methods show that they are equivalent. The converse is not necessarily true.
Use the same properties in both equals() and hashCode() method implementations so that their contract doesn’t violate when any properties are updated.
14) Favour records (since java14) for immutable data
public final class Person {
private final String name;
private final long idNumber;
public Person(String name, long idNumber) {
this.name = name;
this.idNumber = idNumber;
}
public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (other.getClass() != this.getClass()) return false;
Person that = (Person) other;
return (this.name.equals(that.name)) && (this.idNumber == that.idNumber);
}
public String toString() {
return name + " " + idNumber;
}
public int hashCode() {
return Objects.hash(idNumber, name);
}
}
// this class can be transformed to a record:
record Person(String name, long idNumber) { } // cleaner and less verbose
15) For constants, use an enum or final class instead of an interface.
Constants defined in an interface are also accessible using the name of the implementing class and its sub-classes, which can be confusing. Any changes to the value of the constant can potentially affect all the classes that use the interface.
An interface is supposed to define a contract, and constant values are considered implementation details. By defining constants in an interface, we are exposing implementation details, which is not ideal.
To define constants, we can use a final class and define a private constructor to hide the public one, or an enum if the constants have a strong connection. You can find more details on this topic here.
public final class MyValues {
private MyValues() {
// No need to instantiate the class, we can hide its constructor
}
public static final String VALUE1 = "foo";
public static final String VALUE2 = "bar";
}
16) Declare an explicit serial version uid in every serialization class.
If a serializable class doesn’t declare a serialVersionUID, the JVM will generate one automatically at run-time. However, it’s highly recommended that each class declares its serialVersionUID, as the generated one is compiler dependent and thus may result in unexpected InvalidClassExceptions.
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 123123L; // version UID for serialization
private String name;
private int age;
...
}
17) Add an empty line before the annotation
// <-- empty line
@Repository
public class ...
18) The static fields should be placed at the top of the class.
19) For date type handling java.handling.java.localDateTime
is recommended since Java 8.
20) Try to use the suitable type for your variables.
If two or more types satisfy your functional needs, use the type that takes less space in memory if that does not affect maintainability and readability.
int personRunningSpeedKmHour;
byte personRunningSpeedKmHour; // better because a person can not have
// more than 127 km/h as speed
int birthYear;
short birthYear;
// short takes less memory space but in this case int is more readable.
No comments:
Post a Comment