The Elements of Modern Java Style
Code style is an important aspect of software development. When done consistently and correctly, code style can help improve the quality of software.
Join the DZone community and get the full member experience.
Join For FreeCode Style and How to Use It
Compilers and interpreters require the syntax of code to conform to a given programming language’s grammar. Humans, however, require additional guides to translate the cold instructions of machine code into something that can be followed and understood. Code style can provide these cues, and can include things like basic text formatting, indentation, and small methods.
Style can also apply to the use of design patterns, such as best practices for things like constructor chaining, exception handling, and synchronization. Well-styled code is easy to read, like well-written prose. And easier-to-read code means it is easier to understand, and this means more robust, error-free software that developers are happy to work with.
Coding style also works best when it is consistent throughout the whole codebase. Sometimes, however, many years of development may exist where no style or minimal style is applied. In these cases, it is best not to rush in and change everything just for the sake of applying style. Apply changes slowly as code changes, starting first with all new files. Then update existing files only in those places that are changing, such as a method due to a bug fix. And once a particular file passes a threshold (such as 75%), the remaining non-styled sections can be updated.
It is also important to note that modern IDEs allow for the configuration of formatting rules; take advantage of this assistance where available.
Principles of Modern Java Style
The most important aspect of style is simplicity, since the simpler the style the easier it is to remember and apply. No developer wants to memorize hundreds of rules and dozens of exception in order to write software. There is an overall guiding principle that is commonly known as "The Boy Scout Rule for Software Development". This simply states that a developer should leave the code in a better state than when they found it, much like Boy Scouts and their creed to maintain camp sites.
Highlights of Coding and Formatting Conventions
Code Formatting
Formatting consists of all the typological conventions that give code its basic appearance and includes indentation, the use of comments, and where braces appear.
Some best practices:
Nest code and use four spaces for indentation. This gives a good trade-off between emphasizing the text indentation at each level and does not push text too far into the line if multiple indentations levels are required. Use spaces for indentation, never tab characters. Tabs are visually indistinguishable from spaces and can cause issues if both are mixed, since tab lengths can vary across environments. Using only spaces ensure that indentation is always consistent.
Break long lines and margins of 80, 120, or 132 characters. 80 is better for doing side-by-side comparison or multi-way merges, but will lead to more wrapped lines.
Use white space, blank lines, and comments to improve readability.
Braces
Braces have long since become a point of contention in any discussion of coding style. There are two main schools of thought on braces: Cozied and Aligned. Each has its own pros and cons, which are explained below.
Cozied Braces put the open brace right at the end of the originating statement and the closing brace on its own line. Associated keywords (like “else” to an “if”) cozy up to the end brace on the same line.
if (condition) {
statement;
} else {
statement;
}
This style allows for reducing the amount of vertical space used by the code, showing more code on the screen at a time. However, compacting information in this way can reduce readability. Aligned braces, alternatively, place the braces directly on top of each other.
if (condition)
{
statement;
}
else
{
statement;
}
This style sets off statements from the keywords surrounding it by introducing blank lines since each brace is on its own line, instantly improving readability. It also introduces consistency in the location of braces because they are always in the same place, directly below the first character of the statement that introduced them.
Aligned Braces are better for making the code consistent and symmetrical, and this especially becomes obvious when the statement before the first open brace is longer than a single line.
if (long condition with
keyword and keyword and
keyword and keyword)
{
keyword;
}
The cozied brace version of the above requires an additional indentation to differentiate the continuation of the condition with the start of the execution block.
When using Aligned Braces, the formatting automatically aligns the text blocks, keeps the condition items together, and does not require additional formatting rules or indentation spaces. It is for these reasons that Aligned Braces are preferred.
Naming Conventions
Naming is an important part of code writing — picking an appropriate name that conveys meaning and is appropriate for the scope and lifespan of the construct.
In general, it is better to be too descriptive than too terse, but always consider the scope that the variable will exist in. Short names are preferable in smaller scopes, while longer names are more appropriate for longer-lived objects.
A short, single character is appropriate for a self-contained loop:
for (int i = 0; i < listSize; i++)
{
if (condition)
{
sum += list.getItemAt(i);
}
}
Larger-scoped variables require longer and more descriptive names:
private CommandProcessor sequentialCommandProcessor =
new CommandProcessor();
This variable may be used throughout the class in various places, where each of the separate invocations are not simultaneously visible on the editor screen at the same time.
sequentialCommandProcessor.init();
...
sequentialCommandProcessor.addCommand(...);
...
sequentialCommandProcessor.execute();
...
sequentialCommandProcessor.cleanUp();
...
Having a descriptive name reduces the time spent attempting to figure out the intention of the variable. As in the example above, longer variable names use Camel Caps to join words, and never exclude vowels for brevity. Acronyms should only have their first letter capitalized, such as parseXml().
To maintain consistency across the code base, use the following naming conventions:
Capitalize the first letter of Classes and Interfaces.
Start with a lower case letter for methodNames and variableNames.
Constants are in all UPPERCASE_WITH_UNDERSCORES.
Use single words in all lowercase for package names.
Highlights of Programming and Design
Conventions
Programming conventions cover aspects of implementation, including items like Type Safety, Statements and Expressions, Chaining Constructors, Exception Handling, Assertions, Concurrency, Synchronization, and Efficiency.
Some general conventions:
Always use braces for block statements, even if they are empty or a single line; this improves readability and prevents issues if those code blocks are changed in the future.
Use parenthesis to clarify the order of operations.
Use polymorphism to reduce the need for switch statements or the expensive operation and can lead to performance issues if done repeatedly in a loop structure.
If using switch statements, always use a default: case and put break; statements at the end of each block, including the default.
Chaining Constructors
Object construction occurs frequently, and often times with various parameters to help simplify creation. The best practice in this case is to not write duplicate code and instead make each constructor do only the minimum work necessary, passing the remaining processing to the other constructors.
public ChainedConstructor()
{
// Common setup
...
}
public ChainedConstructor(ObjectTypeA a)
{
ChaintedConstrucor();
this.a = a;
}
public ChainedConstructor(ObjectTypeA a, ObjectTypeB b)
{
ChainedConstructor(a);
this.b = b;
}
Exception Handling
One of the most important things a developer can do is ensure that the software never crashes, even in the event of unexpected circumstances. At run-time, many things can go wrong, from invalid user input to network interruptions. It is for this reason that all potential exception cases must be handled.
At the very least, run-time exceptions should always be logged. It is very rare that there is an exception that will truly never occur and can be ignored.
try
{
...
}
catch (IOException e)
{
// Should never reach here
logger.debug(“Unexpected I/O exception:”);
logger.logStackTrace(e);
}
Catch exceptions in as small an exception scope as possible. Do not wrap a try block around a section of code and then only catch java.lang.Throwable. Some exception cases are easier to recover from than others; it is best not to lump non-recoverable errors (such as java.lang.OutOfMemoryError) with more reasonable expected exceptions (such as java.lang.NumberFormatException when converting a String into an Integer).
Synchronization
Synchronization is the enforcement that only a single thread shall have access to a particular portion of code or an object at one moment. The most important rule of maintaining data integrity in a threaded environment is to always allocate and synchronize on an object that is used exclusively for synchronization purposes. Java does provide a mechanism to apply synchronization to classes or methods, but this implicitly or explicitly uses the instance of the class object itself as the synchronization object. That means that all synchronized methods in the class will be blocked if a thread is locked in one of them.
As such, to prevent unintended consequences, always use an Object other than the current object (this) as the synchronization lock token.
private String fileLock = “token”;
public void writeDataFile(String data)
{
synchronized(fileLock)
{
dataFile.write(data);
}
}
public String readDataFile(int lineNumber)
{
String result;
synchronized(fileLock)
{
result = dataFile.read(lineNumber);
}
return result;
}
Synchronization is an expensive operation that will slow down the execution of a code block, so only apply synchronization where necessary to avoid thread collisions or potential data corruption.
Conclusion
Code style is an important aspect of software development. Judiciously and consistently applying a well-defined style will produce code that is simpler to read, understand, and debug, and with fewer defects.
Opinions expressed by DZone contributors are their own.
Comments