A short summary of Java coding best practices

Rafiullah Hamedy
Nov 30, 2018 · 13 min read

The objective of this article is to give you a quick summary of do and don’ts in other words prefer and avoid based on coding standards from tech giants such as Oracle, Google, Twitter and Spring Framework.

You might or might not agree with some of the best practices presented here, and that’s absolutely fine as long as there is some coding standard in place.

Why coding standards in the first place? There are many good reasons if you Google it and I will leave you with the following illustration


Coding standards document can be lengthy and boring. This article cherry picks bits and pieces from coding conventions by Google, Oracle, Twitter and Spring and it’s objective is to provide you with an easy to follow and less boring set of practices to make your code easy to read and maintain.

Almost always you will join teams working on existing software and there is a pretty good chance that most of the authors have left or switched to different projects, leaving you stranded with portions of code that make you question humanity.

Let’s dive into best practices from various coding standards.

Java Source File

The following is considered as best practices when it comes to java source files:

  • The source file length is lower than 2,000 lines of code
  • The source file is organized with documentation comment, package declaration, followed by a class comment, imports grouped (static last), class/interface signature and so on as shown below
package com.example.model;/**
* Implementation-free perspective to be read by developers
* who might not necessarily have the source code at hand
*
* @author x,y,z
* @date
* @version
* @copyright
*
*/
import com.example.util.FileUtil;/*
* Optional class specific comment
*
*/
public class SomeClass {
// Static variables in order of visibility
public static final Integer PUBLIC_COUNT = 1;
static final Integer PROTECTED_COUNT = 1;
private static final Integer PRIVATE_COUNT = 1;
// Instance variables in order of visibility
public String name;
String postalCode;
private String address;
// Constructor and overloaded in sequential order
public SomeClass() {}
public SomeClass(String name) {
this.name = name;
}
// Methods
public String doSomethingUseful() {
return "Something useful";
}
// getters, setters, equals, hashCode and toString at the end
}

Naming

Class and interface names are CamelCase and it is recommended to use the whole word and avoid acronyms/abbreviations. For example class Raster or class ImageSprite

  • Package — names com.deepspace over com.deepSpace or com.deep_space
  • File — names are CamelCase and end with .java matching the class name. There is one public class per file with each top-level class in its file
  • Method — names should be verbs in mixed case with each internal word capitalized for example run(); or runFast();
  • Constants — should be uppercase with “_” separating each words for example int MIN_WIDTH = 44; and int MAX_WIDTH = 99;
  • Variable — a name that tells the reader of the program what the variable represents i.e. if you are storing a test grade then pick grade vs var1 . Keep the variable names short avoid including metadata.
// Prefer (✔️) - variable names short and describe what it stores
int schoolId;
int[] filteredSchoolIds;
int[] uniqueSchooldIds;
Map<Integer, User> usersById;
String value;
//Avoid (x) - Too detailed variable naming
int schoolIdentificationNumber;
int[] userProvidedSchoolIds;
int[] schoolIdsAfterRemovingDuplicates;
Map<Integer, User> idToUserMap;
String valueString;

Remember — variable name should be short and easily tell the reader what value it represents. Use your judgement.


Prefer & Avoid

Formatting and indentation are all about organizing your code to make it easy to read, and it includes spacing, line length, wraps and breaks and so on

  • Indentation — Use 2 or 4 spaces and stay consistent
  • Line length — Up to 70 to 120 characters depending on affect on readability. It’s important to eliminate the need for horizontal scrolling and place line breaks after a comma and operator.

Methods — Here are a listing of best practices

// Prefer (✔️) Line breaks are arbitrary and break after a comma. 
String downloadAnInternet(Internet internet, Tubes tubes,
Blogosphere blogs, Amount<Long, Data> bandwidth) {
tubes.download(internet);
}
// Avoid (x) Hard to diff method args to method body
String downloadAnInternet(Internet internet, Tubes tubes,
Blogosphere blogs, Amount<Long, Data> bandwidth) {
tubes.download(internet);
}
// Prefer (✔️) Add 8 (double of 2 or 4) spaces for deep indent
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg, String yetAnotherArg,
Object andStillAnother) {
...
}
// Prefer (✔️) Easy scanning and extra column space.
public String downloadAnInternet(
Internet internet,
Tubes tubes,
Blogosphere blogs,
Amount<Long, Data> bandwidth) {
tubes.download(internet);
...
}
A unit test would have caught that 😏

If-checks — IMO writing well-formatted code makes it easy to spot typos and errors to the author and the code reviewers, see below:

// Avoid (x) Do not omit {}
if (condition)
statement;
// Avoid (x)
if (x < 0) negative(x);
// Avoid (x)
if (a == b && c == d) {
...
}
// Prefer (✔️)
if ((a == b) && (c == d)) {
...
}
// Prefer (✔️)
if (condition) {
statements;
} else if (condition) {
statements;
} else if (condition) {
statements;
}
// Avoid (x)
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) { //BAD WRAPS
doSomethingAboutIt(); //MAKE THIS LINE EASY TO MISS
}
// Prefer (✔️)
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}

Ternary operator — And below are recommended practices

alpha = (aLongBooleanExpression) ? beta : gamma;alpha = (aLongBooleanExpression) ? beta
: gamma;
alpha = (aLongBooleanExpression)
? beta
: gamma;

Switch — When it comes to switch it’s best practice to

  • Always have a default case even without code
  • Use /* falls through */ to indicate the control falls to next case
switch (condition) {
case ABC:
statements;
/* falls through */
case DEF:
statements;
break;
default:
statements;
break;
}

Exception messages — When throwing an exception here are samples of good and poorly indented messages.

// Avoid (x) - Not easy to read
throw new IllegalStateException("Failed to process request" + request.getId()
+ " for user " + user.getId() + " query: '" + query.getText()
+ "'");
// Prefer (✔️) - Fairly easier to read
throw new IllegalStateException("Failed to process"
+ " request " + request.getId()
+ " for user " + user.getId()
+ " query: '" + query.getText() + "'");

Iterators and Streams — Streams are becoming more common and at times it can be very complex hence, it’s important to indent for easy to read.

// Avoid (x) - Not easy to read
Iterable<Module> modules = ImmutableList.<Module>builder().add(new LifecycleModule())
.add(new AppLauncherModule()).addAll(application.getModules()).build();
// Prefer (✔️) - Fairly easier to read
Iterable<Module> modules = ImmutableList.<Module>builder()
.add(new LifecycleModule())
.add(new AppLauncherModule())
.addAll(application.getModules())
.build();
Just follow a coding standard — any really

Declarations and Assignments— One declaration per line is recommended since it encourages comments as shown below.

// Prefer (✔️) 
int level; // indentation level
int sizeMeter; // size of table
// Avoid (x) in favour of above
int level, sizeMeter;
// Prefer (✔️) - Include unit in variable name or type
long pollIntervalMs;
int fileSizeGb;
Amount<Integer, Data> fileSize;
// Avoid (x) mixing types
int foo, fooarray[];
// Avoid (x) - Do not separate with comma
Format.print(System.out, “error”), exit(1);
// Avoid (x) multiple assignment
fooBar.fChar = barFoo.lchar = 'c';
// Avoid (x) embedded assignments in attempt to increase performance // or save a line. I am guilty of doing this :(
d = (a = b + c) + r;
// Prefer (✔️) over above
a = b + c;
d = a + r;
// Prefer (✔️)
String[] args
// Avoid (x)
String args[]
// Prefer (✔️) Long use "L" instead of "l" to avoid confusion with 1
long timeout = 3000000000L;
// Avoid (x) - Hard to tell last letter is l and not 1
long timeout = 3000000000l;

Put declarations only at the beginning of blocks (A block is code surrounded by curly braces { and }). Do not wait to declare variables until their first use; it can confuse the unwary programmer and hamper code portability within the scope.

// Prefer (✔️) declare at the beginning of the block. 
public void doSomething() {
int whatIRepresent; // beginning of method block
if (condition) {
int someFlag; // beginning of “if” block

}
}

It’s also important to avoid local declarations that hide declarations of the higher-levels and is to avoid confusions as shown below

int count;
...
public void doSomething() {
if (condition) {
int count; // AVOID!
...
}
...
}

Spacing & line breaks — Avoid the temptation of saving 1–2 lines of code at the expense of readability. Here are all the best practices when it comes to spacing and blank lines (A white space does make a difference)

  • One (1) blank line between methods and Spring developers recommends two (2) blank lines after constructors, static block, fields and inner class
  • Space pad operators i.e. Use int foo = a + b + 1; over int foo=a+b+1;
  • Separate all binary operators except “.” from operands using a space
  • Open brace “{” appears at the end of the same line as the declaration statement or method and closing brace “}” starts a line by itself indented
// Prefer (✔️) - Space after "while" and before "("
while (true) {
...
}
// Avoid (x) - Unlike above no space
while(true) {
...
}
// Prefer (✔️) - No space between "doSomething" and "("
public void doSomething() {
...
}
// Avoid (x) - Unlike above space
public void doSomething () {
...
}
// Prefer (✔️) - Add a space after an argument
public void doSomething(int a, int b) {
...
}
// Prefer (✔️) - Space between operand and operators (i.e. +, =)
a += c + d;
a = (a + b) / (c * d);
while (d++ = s++) {
n++;
}

Documentation and Comments

It’s worth mentioning that almost all code goes changes throughout its lifetime and there will be times when you or someone is trying to figure out what a complex block of code, a method, or a class is intended to do unless clearly described. The reality is almost always as follow

There are times that the comment on a complex piece of code, method, class does not add any value or serve its purpose. This usually happens when commenting for the sake of it.

Comments should be used to give overviews of code and provide additional information that is not readily available in the code itself. Let’s get started. There are two types of comments

Implementation Comments — are meant to comment out code or comment about a particular implementation of the code.

Documentation Comments — are meant to describe the specification of the code from an implementation-free perspective to be read by developers who might not necessarily have the source code at hand.

The frequency of comments sometimes reflects poor quality of code. When you feel compelled to add a comment, consider rewriting the code to make it clearer.

Types of Implementation Comments

There are four (4) types of implementation comments as shown below

  • Block comment — see example below
  • Single line comment — when the comment is not longer than a line
  • Trailing comments — Very short comment moved to the right end
  • End of line comment — begins a comment that continues to the newline. It can comment out a complete line or only a partial line. It shouldn’t be used on consecutive multiple lines for text comments; however, it can be used in consecutive multiple lines for commenting out sections of code.
// Block comment
/*
* Usage: Provides description of files, methods, data structures
* and algorithms. Can be used at the beginning of each file and
* before each method. Used for long comments that do not fit a
* single line. 1 Blank line to proceed after the block comment.
*/
// Single line comment
if (condition) {
/* Handle the condition. */
...
}
// Trailing comment
if (a == 2) {
return TRUE; /* special case */
} else {
return isPrime(a); /* works only for odd a */
}
// End of line comment
if (foo > 1) {
// Do a double-flip.
...
} else {
return false; // Explain why here.
}
//if (bar > 1) {
//
// // Do a triple-flip.
// ...
//}
//else
// return false;

Documentation comments (i.e. Javadoc)

Javadoc is a tool that generates HTML documentation form your java code using the comments that begin with /** and end with */ — see Wikipedia for more details on how Javadoc works or just read along.

Here is an example of Javadoc

/**
* Returns an Image object that can then be painted on the screen.
* The url argument must specify an absolute {@link URL}. The name
* argument is a specifier that is relative to the url argument.
* <p>
* This method always returns immediately, whether or not the
* image exists. When this applet attempts to draw the image on
* the screen, the data will be loaded. The graphics primitives
* that draw the image will incrementally paint on the screen.
*
* @param url an absolute URL giving the base location of the image
* @param name the location of the image, relative to the url argument
* @return the image at the specified URL
* @see Image
*/
public Image getImage(URL url, String name) {
try {
return getImage(new URL(url, name));
} catch (MalformedURLException e) {
return null;
}
}

And the above would result into a HTML as follow when javadoc is run against the code that has the above

See here for more

Here are some key tags that you can use to enhance the quality of the generated java documentation.

@author              => @author Raf
@code => {@code A<B>C}
@deprecated => @deprecated deprecation-message
@exception => @exception IOException thrown when
@link => {@link package.class#member label}
@param => @param parameter-name description
@return => What the method returns
@see => @see "string" OR @see <a ...></a>
@since => To indicate the version when a publicly accessible method is added

For a complete listing and more detailed description see here

Twitter’s coding standard advises against the use of @author tag

Code can change hands numerous times in its lifetime, and quite often the original author of a source file is irrelevant after several iterations. We find it’s better to trust commit history and OWNERS files to determine ownership of a body of code.

Following are examples of how you could write a documentation comment that is insightful as described in Twitter’s coding standard

// Bad.
// - The doc tells nothing that the method declaration didn't.
// - This is the 'filler doc'. It would pass style checks, but
doesn't help anybody.
/**
* Splits a string.
*
* @param s A string.
* @return A list of strings.
*/
List<String> split(String s);
// Better.
// - We know what the method splits on.
// - Still some undefined behavior.
/**
* Splits a string on whitespace.
*
* @param s The string to split. An {@code null} string is treated as an empty string.
* @return A list of the whitespace-delimited parts of the input.
*/
List<String> split(String s);
// Great.
// - Covers yet another edge case.
/**
* Splits a string on whitespace. Repeated whitespace characters
* are collapsed.
*
* @param s The string to split. An {@code null} string is treated as an empty string.
* @return A list of the whitespace-delimited parts of the input.
*/
List<String> split(String s);

It’s important to be professional when it comes to writing comments

// Avoid (x)
// I hate xml/soap so much, why can't it do this for me!?
try {
userId = Integer.parseInt(xml.getField("id"));
} catch (NumberFormatException e) {
...
}
// Prefer (✔️)
// TODO(Jim): Tuck field validation away in a library.
try {
userId = Integer.parseInt(xml.getField("id"));
} catch (NumberFormatException e) {
...
}

And it is important to keep in mind not to document overrided method unless the implementation has changed.


And here are a few more points to keep in mind

  • Avoid wildcard imports — as described in Twitter’s coding standards it makes the source of class less clear. I work in a team with a mix of Eclipse and IntelliJ users and I found out that Eclipse removes wildcard imports and IntelliJ introduce it. There probably is an option to turn it off, just wanted to point out the default for the two.
  • Always use @Override annotation when overriding
  • Encourage use of @Nullable when a field or method returns null
  • Make use of special comments for future work and do not forget to leave a reference to yourself so others know who to ask their Y question instead of guessing, removing it or checking git blame to find who added it. Some IDEs like Eclipse and IntelliJ also help in listing these for easy access as well as a reminder.
//FIXME (Raf): An actionable message describe what needs to be done
//TODO (Raf): An actionable message describe what needs to be done

The end game is to write code that make the life of future authors and maintainers easy.

The end game

Rafiullah Hamedy

Written by

Senior software developer | Co-founder in making | Traveller & blockchain enthusiast

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade