When we talk about Java best practices, we often learn about clean code. It is all about making your code easy to read and understand for yourself and others. When your code is well-written, organized and standard, you don't have to explain it in detail to your teammates because it speaks for itself.
The “Standard” refers to the norms developed by the developer’s community on how to write different aspects of code.
It’s needless to explain the benefits associated with clean code writing practice; as a coder you might be aware of them. However, if we have to summarise the key benefits of well-written code, below are the key advantages:
Standard or clean code is a set of practices commonly used by professional developers. These practices assist individuals in creating well-organized and tidy code. In this article, we will walk you through the methods that will help you master the art of clean code writing and give you a clear understanding of what clean code is all about.
Yes, It matters a lot. Especially in today’s digital world, when apps are getting bigger and more complex, so are our codebases. Therefore it becomes essential to develop a habit of writing clean code.
Remember, your job is not writing code that is only understandable to a machine; it should be easier to read for humans.
These three acronyms represent three valuable rules of thumb in software development as the following:
Keep It Simple, Stupid: The simplest solution is often the best one.
When you're writing code, don't try to be too clever. You want to be innovative, but don't let that get in the way of writing good code. Over-engineering can make your code harder to understand.
The principle of KISS is all about keeping things simple. Let’s understand it with an example; you're developing a method in Java that calculates the sum of two numbers. Following the KISS principle, the code would look like this:
public int calculateSum(int a, int b) {
return a + b;
}
Now the complex implementation is as the following;
public static int calculateSum(int... a) {
int result = 0;
for (int i = 0; i < a.length; i++) {
result += a[i];
}
return result;
Don’t Repeat Yourself – Avoid duplicating parts of code by putting it into a single system or a method.
Simply put, always try to write reusable logic, functions, components, modules, etc. Let’s understand it with this example: Without adhering to dry principle:
public class AreaCalculatorWithoutDry {
public static void main(String[] args) {
// Calculate areas
double rectangleLength = 5;
double rectangleWidth = 8;
double rectangleArea = rectangleLength * rectangleWidth;
System.out.println("The area of the rectangle is: " + rectangleArea);
double circleRadius = 3;
double circleArea = 3.14 * circleRadius * circleRadius;
System.out.println("The area of the circle is: " + circleArea);
// More code...
// Later in the code, you need to calculate the area of another rectangle
double anotherRectangleLength = 6;
double anotherRectangleWidth = 10;
double anotherRectangleArea = anotherRectangleLength * anotherRectangleWidth;
System.out.println("The area of the rectangle is: " + anotherRectangleArea);
// More code...
}
}
Code adhering to dry principle:
public class AreaCalculatorWithDry {
public static double calculateRectangleArea(double length, double width){
return length * width;
}
public static double calculateCircleArea(double radius) {
return 3.14 * radius * radius;
}
public static void main(String[] args) {
// Calculate areas
double rectangleLength = 5;
double rectangleWidth = 8;
double rectangleArea = calculateRectangleArea(rectangleLength, rectangleWidth);
System.out.println("The area of the rectangle is: " + rectangleArea);
double circleRadius = 3;
double circleArea = calculateCircleArea(circleRadius);
System.out.println("The area of the circle is: " + circleArea);
// More code...
// Later in the code, you need to calculate the area of another rectangle
double anotherRectangleLength = 6;
double anotherRectangleWidth = 10;
double anotherRectangleArea = calculateRectangleArea(anotherRectangleLength, anotherRectangleWidth);
System.out.println("The area of the rectangle is: " + anotherRectangleArea);
// More code...
}
}
You Ain’t Going to Need It – It advocates the principle of implementing the only required functionality in development.
Avoid adding unnecessary (or extra) features. In simple words, build only what you need now.
import java.util.HashMap;
import java.util.Map;
public class ShoppingCart {
private Map<String, Double> items;
public ShoppingCart() {
this.items = new HashMap<>();
}
public void addItem(String itemName, double price) {
items.put(itemName, price);
}
public void removeItem(String itemName) {
items.remove(itemName);
}
public double calculateTotal() {
double total = 0;
for (double price : items.values()) {
total += price;
}
return total;
}
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// Adding items to the cart
cart.addItem("Shirt", 25.99);
cart.addItem("Jeans", 39.99);
// Calculating the total price
double total = cart.calculateTotal();
System.out.println("Total price: $" + total); // Output: Total price: $65.98
// Removing an item
cart.removeItem("Shirt");
// Recalculating the total price after removing an item
total = cart.calculateTotal();
System.out.println("Updated total price: $" + total); // Output: Updated total price: $39.99
}
}
In this example, we have created a simple ShoppingCart class that allows users to add items, remove items, and calculate the total price. We have not included unnecessary features like handling quantities, discounts, payment methods, or shipping options, as the focus is on building only what is needed for a basic shopping cart functionality.
Each module or class in your code should only do one thing. It is known as the “Single Responsibility Principle” (SPR). When a class or method has a single responsibility, it becomes easier to maintain, understand, and test the codebase. Having multi-functionality can affect unrelated parts of the program. So, if you find yourself writing a module or class that does too many things, break it up into smaller, more focused modules or classes.
Ensure that each method has a single responsibility. Split complex methods into smaller, focused ones, which improves code readability, testability, and reusability. For example:
// Avoid
public void processStudentData() {
// Lengthy code block
}
// Prefer
public void calculateAverageGrade() {
// Code to calculate average grade
}
public void updateStudentRecord() {
// Code to update student record
}
A commit is a snapshot of the changes made to a repository at a specific point in time. It helps developers track changes over time. Therefore, it should be clear, concise (keep it under 50 characters if possible), and meaningful. Start your commit message with a capitalized verb that describes the change you made, e.g., "Fix bug with global search input deleting user text."
Meaningful and consistent naming is always important, which makes code more readable.
For example, consider the following variable names.
int a = 10;
double b = 3.14;
String c = "Hello";
These names are too short and vague. They do not tell us anything about what they store or what they are used for. A better way to name them would be:
int age = 10;
double pi = 3.14;
String greeting = "Hello";
Conventions are not strict rules. It refers to a set of agreed-upon rules, practices, or guidelines that developers follow to maintain consistency and improve code readability. It involves appropriately naming variables, functions, classes, and other elements.
5.1. Packages: Package names should be in lowercase, with multiple words separated by periods. For example com.Example.MyApp.
Package Name: com.example.myapp [Good practice]
Package Name: com.Example.MyApp [Bad practice]
5.2. Classes and Interfaces: Class and interface names should be written in CamelCase, starting with an uppercase letter. For example: MyClass, MyInterface is a proper way of naming classes and interfaces. While, Myclass, Myinteface can lead to confusion and may make code harder to understand.
5.3. Methods: Method names should be written in camelCase, starting with a lowercase letter. For example: calculateArea(), sendMessage().
Good Practice:
Bad Practice:
5.4 Variables: Variable names should also be in camelCase, starting with a lowercase letter. For example: firstName, age, totalAmount.
Good Practice:
Bad Practice:
5.5 Constants: Constant names should be written in uppercase letters, with words separated by underscores. For example: MAX_VALUE, DEFAULT_TIMEOUT, PI.
Good Practice:
Bad Practice:
5.6 Enums: Enum names should be written in CamelCase, starting with an uppercase letter. Enum constants should be written in uppercase letters, separated by underscores. For example: enum DayOfWeek { MONDAY, TUESDAY, WEDNESDAY, THURSDAY,FRIDAY, SATURDAY, SUNDAY }
Good Practice:
enum DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Bad Practice:
enum dayOfWeek {
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
enum DayOfWeek {
monday, tuesday, wednesday, thursday, friday, saturday, sunday
}
Comments are notes that programmers add to their code, explaining what the code does. The machine ignores comments, but humans can read them. They are really helpful for finding specific sections of code. If you've properly commented on your code, you will easily find the section you're looking for, even if you last saw it a while ago.
Documenting your code with comments is essential, especially when working on a project with others or planning to revisit your code. Comments can help others understand your code and also aid you in remembering what your code does.
In Java programming, comments are of two types: single-line and multi-line.
Here are some examples of single-line and multi-line comments:
// This is a single-line comment
/* This is a multi-line comment
that can span multiple lines */
For example, consider the following code:
// This method calculates the area of a circle
public static double calculateArea(double radius) {
// The formula for the area of a circle is pi * r^2
return Math.PI * Math.pow(radius, 2);
}
This code has too many comments that are redundant or obvious. A better way to write it would be:
// Calculates the area of a circle given its radius
public static double calculateArea(double radius) {
return Math.PI * Math.pow(radius, 2);
}
It is a widely followed convention in object-oriented programming languages. Ordering class members by scope makes the program readable. In Java, class members have four scopes: public, protected, default, and private. Public members can be accessed from anywhere, protected members can be accessed from the same package or subclasses, default members can only be accessed from the same package, and private members can only be accessed from the same class.
A good convention for ordering class members by scope is to put public members first, then protected members, then default members, and then private members. You can also put constructors and nested classes in their sections.
You can also use the IDE plugin or code formatter tools to arrange and organize the members based on their scope. You can use the Eclipse Code Formatter plugin for Eclipse or the Reformat Code feature for IntelliJ IDEA.
Encapsulation is one of the fundamental principles of object-oriented programming languages. It refers to bundling data and methods and only exposing the select information.
You can achieve encapsulation by making your class members (fields and methods) private by default. This means they can only be accessed within the same class. If you need to expose some of your class members to other class, you can provide public or protected accessors(getters and setters) or other methods to define the interface of your class.
By making your class members private, you ensure that all the elements inside the class stick together nicely and work well as a team. It also helps prevent outsiders from messing with the class's inner workings. Plus, reducing how much the class relies on other stuff makes testing and maintaining your code a whole lot easier!
For example, consider this class that represents a bank account:
public class BankAccount {
// Bad practice: exposing fields as public
public String owner;
public double balance;
// Good practice: making fields private and providing accessors
private String owner;
private double balance;
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}
In the above example, making the fields private and providing accessors ensures that the owner and balance are only accessible and modifiable through the defined methods. This way, you can enforce some validation and logic on the bank account's operation.
Java has a cool feature that lets you make your numeric more readable. If you don’t know, you can underscore the lengthy numbers that look more readable(it’s like separating numbers, so it becomes easier to read if it is long).
It's especially handy for dealing with large numbers or when working with binary, octal, or hexadecimal values. By using underscores, you can separate digits and create more organized and legible numeric representations.
For example, you can write:
// Without underscores
long creditCardNumber = 1234567890123456L;
long socialSecurityNumber = 999000111L;
float pi = 3.141592653F;
long hexBytes = 0xFFECDE5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
// With underscores
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_00_0111L;
float pi = 3.14_15_92_65_3F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
As you can see, using underscores in numeric literals makes them easier to read and compare. You can also logically group the digits, such as by thousands, bytes, words, etc.
However, some rules and limitations exist for using underscores in numeric literals. For example:
Clean code helps in writing maintainable and bug-free code. By following the outlined practices in this blog post, you can write code that is easy to understand and maintain. Furthermore, we will discuss more Java best practices in part two of this series. We hope you find this article helpful and informative.
Java best practices are the golden rules of coding in Java. They help make your code clean, efficient, and easy to work with. Following these practices is important because they ensure your code is readable, scalable, and performs well.
Coding standards are the style guide for your Java code. They cover things like how you name your variables, how you format your code, and how you write comments. For example, using meaningful variable names like `totalPrice` instead of `x` and keeping your code neatly indented and easy to read are some common coding standards.
SOLID principles and design patterns are like the secret sauce of Java coding. They help you write code that's modular, flexible, and easy to maintain. For instance, SOLID principles guide you on things like keeping each class focused on one job and making your code open to extension but closed to modification.
Performance optimization and memory management - every Java developer's favorite topics. Best practices here involve things like being mindful of how many objects you create (because too many can slow things down), choosing the right data structures and algorithms for the job (because some are faster than others), and being smart about how you use memory (because nobody likes memory leaks).
You might also like
Get In Touch
Contact us for your software development requirements