DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Deadlock-Free Synchronization in Java
  • How to Secure Apache Ignite From Scratch
  • Java Thread Synchronization and Concurrency Part 1
  • Advanced Brain-Computer Interfaces With Java

Trending

  • Zero Trust for AWS NLBs: Why It Matters and How to Do It
  • How to Convert XLS to XLSX in Java
  • Why Documentation Matters More Than You Think
  • Optimize Deployment Pipelines for Speed, Security and Seamless Automation
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Java Best Practices Quick Reference

Java Best Practices Quick Reference

This tutorial includes the best practices and references of Java to enhance the readability and reliability of your Java code.

By 
Alexsandro Souza user avatar
Alexsandro Souza
·
Dec. 17, 20 · Tutorial
Likes (20)
Comment
Save
Tweet
Share
17.0K Views

Join the DZone community and get the full member experience.

Join For Free

Developers have a big responsibility to make the right decision every day and the best thing to help make good decisions is the experience. Not everyone has long experience in software development, but everyone can leverage others' experience. These are some tips that I have acquired with my experience in working with Java and I hope it can help you to improve the readability and reliability of your Java code.

Programing Principles

Don’t write code that only works. Aim to write code that can be maintained — not only by yourself but by anyone else who may end up working on the software at some point in the future.

80 percent of the time a developer is reading code and 20 percent writing and testing the code. So, please focus on writing readable code!

Your code should not need comments to understand what it is doing!

To help us to develop good code, there are many programming principles that we can use as guidelines. Below I will list the most important ones.

  • KISS — It stands for “Keep It Simple, Stupid”. You may notice that developers at the beginning of their journey try to implement complicated, ambiguous design.
  • DRY — “Don’t Repeat Yourself”. Try to avoid any duplicates, instead, you put them into a single part of the system or a method.
  • YAGNI — “You Ain’t Gonna Need It”. If you run into a situation where you are asking yourself, “What about adding extra (feature, code, …etc.) ?”, you probably need to re-think it.
  • Clean code over clever code — Speaking of clean code, leave your ego at the door, and forget about writing clever code.
  • Avoid premature optimization — The problem with premature optimization is that you can never really know where a program’s bottlenecks will be until after the fact.
  • Single responsibility — Every class or module in a program should only concern itself with providing one bit of specific functionality.
  • Composition over Inheritance — Objects with complex behaviors should do so by containing instances of objects with individual behaviors rather than inheriting a class and adding new behaviors.
  • Object calisthenics — Object Calisthenics are programming exercises, formalized as a set of 9 rules
  • Fail fast, fail hard — The fail-fast principle stands for stopping the current operation as soon as any unexpected error occurs. Adhering to this principle generally results in a more stable solution

Packages

  1. Favor structuring packages by domain concerns rather than technical layers.
  2. Favor layouts that promote encapsulation and information hiding to protect against improper usage over organizing classes by technical concerns.
  3. View packages as providing a strict API — do not expose the inner workings (classes) that are meant for internal processing only.
  4. Not public access scope for classes that are supposed to be only used inside the package.

Classes

Static

  1. Do not allow instantiation of a static class. Always create a private constructor.
  2. Static classes should be stateless, immutable, not allow subclassing, and thread-safe.
  3. Static classes should be side-effect free and provided as utilities, such as filtering a list.

Inheritance

  1. Prefer composition over inheritance.
  2. Do not expose protected fields. Provide a protected accessor instead.
  3. If a class variable can be marked final, make it.
  4. If inheritance is not expected, make the class final.
  5. Mark a method final unless subclasses are expected to be allowed to override it.
  6. If no constructor is required do not create a default one with no implementation logic. Java will automatically provide a default constructor if none is specified.

Interfaces

  1. Do not use the constant interface pattern (an interface of constants) as it allows classes to implement and dirty up the API. Use a static class instead. This has the added benefit of allowing you to perform more complex object initialization in a static block (such as populating a Collection).
  2. Avoid Interface overuse
  3. Having one and only one class implement an interface is likely to overuse interfaces and it does more harm than good. More
  4. “Program to an interface, not to an implementation” doesn’t mean you should pair each and every one of your domain classes with a more or less identical interface, doing so, you are violating YAGNI
  5. Always keep interfaces small and specific so that clients will only have to know about the methods that are of interest to them. Check out the ISP from SOLID.

Finalizers

  1. Object#finalize() should be used judiciously and only as a fail-safe for resource clean-up (e.g. closing a file). Always provide an explicit clean-up method (e.g. close()).
  2. In an inheritance hierarchy, always call the parent’s finalize() within a try block. The class’s cleanup should be in the finally block.
  3. If the explicit clean-up method was not called and the finalizer closed the resources, then log this error.
  4. If a logger is not accessible then use the Thread’s exception handler (which eventually delegates to standard error which is captured in the logs).

General

Assertions

An assertion, usually in the form of a precondition check, enforce the type’s contract in a fail-fast, fail-hard manner. They should be used liberally to catch programming errors as close to the source as possible.

Object state:

  • An object should never be constructed or transition into an invalid state.
  • On constructors and methods, always describe and enforce the contract through validations.
  • The Java keyword assert should be avoided as it can be disabled and is generally a brittle construct.
  • Use the Assertions utility class to avoid verbose if-else conditions for precondition checks.

Generics

A full, extremely detailed explanation is available in the Java Generics FAQ. Below are the common scenarios that developers should be aware of.

  1. When possible, prefer using type inference rather than returning a base class/interface:
Java
 




xxxxxxxxxx
1


 
1
// MySpecialObject o = MyObjectFactory.getMyObject();
2
public <T extends MyObject> T getMyObject(int type) { 
3
 return (T) factory.create(type);
4
}



2. When a type cannot automatically be inferred, inline it.

Java
 




xxxxxxxxxx
1
13


 
1
public class MySpecialObject extends MyObject<SpecialType> {
2

          
3
  public MySpecialObject() {
4

          
5
   super(Collections.emptyList());   // This is ugly, as we loose type
6
   super(Collections.EMPTY_LIST();    // This is just dumb
7

          
8
   // But this is beauty
9
   super(new ArrayList<SpecialType>());    
10
   super(Collections.<SpecialType>emptyList());
11
  }
12

          
13
}



3. Wildcards:

Use an extended wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure and don’t use a wildcard when you do both.

  1. Everyone loves PECS! (Producer-extends, Consumer-super)
  2. Use Foo<? extends T> for a T producer.
  3. Use Foo<? super T> for a T consumer.

Singletons

A singleton should never be written in the classic Design Patterns style, which is quite valid in C++ (as it was written with) but inappropriate in Java.

  1. While correctly thread-safe, never implement as follows. (This has been a performance bottleneck!)
Java
 




xxxxxxxxxx
1
12


 
1
public final class MySingleton {
2
  private static MySingleton instance;
3
  private MySingleton() {
4
    // singleton
5
  }
6
  public static synchronized MySingleton getInstance() {
7
    if (instance == null) {
8
      instance = new MySingleton();
9
    }
10
    return instance;
11
  }
12
}



2. If lazy initialization is truly desirable, then a combination of the two approaches will do the job!

Java
 




xxxxxxxxxx
1
11


 
1
public final class MySingleton {
2
  private MySingleton() {
3
   // singleton
4
  }
5
  private static final class MySingletonHolder {
6
    static final MySingleton instance = new MySingleton();
7
  }  
8
  public static MySingleton getInstance() {
9
    return MySingletonHolder.instance;
10
  }
11
}



3. Spring: By default, a bean is registered with a singleton scope, meaning that only one instance will be created by the container and be wired to all consumers. This provides the same semantics as a normal Singleton, without the performance or coupling limitations.

Exceptions

1. Use checked exceptions for recoverable conditions and run-time exceptions for programming errors. Example: Getting an Integer from a String.

  • Bad: NumberFormatException extends RuntimeException, so it is meant to indicate programming errors.
  • Do not do the following:
Java
 




xxxxxxxxxx
1
13


 
1
// String str = input string
2
Integer value = null;
3
try {
4
   value = Integer.valueOf(str);
5
} catch (NumberFormatException e) {
6
// non-numeric string
7
}
8

          
9
if (value == null) {
10
// handle bad string
11
} else {
12
// business logic
13
}



  • Correct usage:
Java
 




xxxxxxxxxx
1


 
1
// String str = input string
2
// Numeric string with at least one digit and optional leading negative sign
3

          
4
if ( (str != null) && str.matches("-?\\d++") ) {  
5
   Integer value = Integer.valueOf(str);
6
  // business logic
7
} else {
8
  // handle bad string
9
}



2. You should handle exceptions in the right place, the right place is at the domain level.

  • WRONG WAY — The data object layer doesn’t know what to do when there is a database exception.
Java
 




xxxxxxxxxx
1
14


 
1
class UserDAO{
2
    public List<User> getUsers(){
3
        try{
4
            ps = conn.prepareStatement("SELECT * from users");
5
            rs = ps.executeQuery();
6
            //return result
7
        }catch(Exception e){
8
            log.error("exception")
9
            return null
10
        }finally{
11
            //release resources
12
        }
13
    }}
14

          



  • RECOMMENDED WAY — The data layer should just rethrow the exception and transfer the responsibility to handle the exception or not to the right layer.
Java
 




xxxxxxxxxx
1
16


 
1
=== RECOMMENDED WAY ===
2
Data layer should just retrow the exception and transfer the responsability to handle the exception or not to the right layer.
3
class UserDAO{
4
 
5
    public List<User> getUsers(){
6
       try{
7
          ps = conn.prepareStatement("SELECT * from users");
8
          rs = ps.executeQuery();
9
          //return result
10
       }catch(Exception e){
11
        throw new DataLayerException(e);
12
       }finally{
13
          //release resources
14
       }
15
   }
16
}



3. Exceptions should in general NOT be logged at the point they are thrown, but rather at the point they are actually handled. Logging exceptions when they are thrown or rethrown tends to fill the log files with noise. Also, note that the exception stack trace captures where the exception was generated anyway.

4. Favour the use of standard exceptions

5. Use Exceptions rather than Return codes.

Equals and HashCode

There are a number of concerns to be aware of for writing proper object equivalence and hash code methods. To simplify usage, use java.util.Objects’ equals and hash.

Java
 




xxxxxxxxxx
1
25


 
1
public final class User {
2

          
3
  private final String firstName;
4
  private final String lastName;
5
  private final int age;
6
 
7
  ...
8
 
9
  public boolean equals(Object o) {
10
    if (this == o) {
11
      return true;
12
    } else if (!(o instanceof User)) {
13
      return false;
14
    }
15
    User user = (User) o;
16
    return Objects.equals(getFirstName(), user.getFirstName()) &&                                 
17
     Objects.equals(getLastName(),user.getLastName()) &&
18
     Objects.equals(getAge(), user.getAge());
19
  }
20
 
21
  public int hashCode() {
22
    return Objects.hash(getFirstName(),getLastName(),getAge());
23
  }
24
}
25

          



Resource Management

  1. Methods for safely releasing resources:
  2. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
Java
 




xxxxxxxxxx
1


 
1
private doSomething() {
2
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
3
  try {
4
    // business logic
5
  }
6
}



Provide Shutdown Hooks

Provide a shutdown hook to be called if the JVM is gracefully terminated. (This will not handle abrupt terminations, such as due to a power outage)

This is the recommended alternative instead of declaring a finalize() method, which will only be run if System.runFinalizersOnExit() is true (by default it is false).

Java
 




xxxxxxxxxx
1
36


 
1
public final class SomeObject {
2

          
3
  var distributedLock = new ExpiringGeneralLock ("SomeObject", "shared");
4
 
5
  public SomeObject() {
6
    Runtime
7
      .getRuntime()
8
      .addShutdownHook(new Thread(new LockShutdown(distributedLock)));
9
  }
10
 
11
  /** Code may have acquired lock across servers */
12
  ...
13
 
14
  /** Safely releases the distributed lock. */
15
  private static final class LockShutdown implements Runnable {
16
    private final ExpiringGeneralLock distributedLock;
17
 
18
    public LockShutdown(ExpiringGeneralLock distributedLock) {
19
      if (distributedLock == null) {
20
        throw new IllegalArgumentException("ExpiringGeneralLock is null");
21
      }
22
      this.distributedLock = distributedLock;
23
    }
24
 
25
    public void run() {
26
      if (isLockAlive()) {
27
        distributedLock.release();
28
      }
29
    }
30
 
31
    /** @return True if the lock is acquired and has not expired yet. */
32
    private boolean isLockAlive() {
33
      return distributedLock.getExpirationTimeMillis() > System.currentTimeMillis();
34
    }
35
  }
36
}



Allow resources to expire (and also be renewable) is shared between servers. (This allows recovery from abrupt termination, such as power outages).

See code sample above, which uses an ExpiringGeneralLock (a lock that is shared across systems).

Date-Time

Java 8 introduces a new date-time API under the package java.time. With Java 8, a new Date-Time API is introduced to cover the following drawbacks of old date-time API: Not thread-safe, Poor design, Difficult time zone handling and etc.

Concurrency

General

  1. Beware of the following libraries, which are surprisingly not thread-safe. Always synchronize against the objects if shared between multiple threads.
  2. Date (not immutable) — Use the new Date-time API that is thread-safe;
  3. SimpleDateFormat — Use the new Date-time API that is thread-safe;
  4. Prefer using java.util.concurrent.atomic classes over making variables volatile.
  5. The behavior of the atomic classes is more obvious to the average developer, whereas volatile requires understanding the Java Memory Model.
  6. The atomic classes wrap volatile variables in a more user-friendly interface.
  7. Understand the use-cases where volatile is appropriate. (see article)
  8. Use Callable<Void> when requiring a checked exception but there is no return type. As Void cannot be instantiated, this communicates the intent and can return null safely.

Threads

  1. java.lang.Thread should be considered depreciated. While it is not, officially, in almost all instances the java.util.concurrent package provides a cleaner solution to the problem.
  2. It is considered poor practice to extend java.lang.Thread — instead implement Runnable and create a new thread with the instance in the constructor (rule of composition over inheritance).
  3. Prefer executors and streams when required concurrent processing
  4. It is always a good idea to specify your own custom thread factory to control the configuration of the threads being created. More
  5. Use DaemonThreadFactory in Executors for non-critical threads so that the thread pool can be shut down immediately on server shutdown. more
Java
 




xxxxxxxxxx
1


 
1
this.executor = Executors.newCachedThreadPool((Runnable runnable) -> {
2
    Thread thread = Executors.defaultThreadFactory().newThread(runnable);
3
    thread.setDaemon(true);
4
    return thread;
5
});



  1. Java synchronization is no longer slow (55–110ns). Do not avoid it by using broken tricks like double-checked locking.
  2. Prefer synchronizing against an internal object, rather than the class, as users may synchronize against your class/instance.
  3. Always synchronize multiple objects in the same order to avoid deadlocks.
  4. Synchronizing against the class does not inherently block access to its internal objects. Always use the same locks when accessing a resource.
  5. Beware that the synchronized keyword is not considered part of a method’s signature and will thus not be inherited.
  6. Avoid excessive synchronization, it can cause reduced performance and deadlock. Use the synchronized keyword strictly to the piece of code that requires synchronization.

Collections

  1. Use Java-5 concurrent collections when possible in multi-threaded code. These are safe and have superior performance.
  2. Use CopyOnWriteArrayList over synchronizedList when suitable
  3. Use Collections.unmodifiable list(…) or copy the collection when receiving it as a parameter new ArrayList(list). Avoid having local Collections changed from outside your class.
  4. Always return a copy of your collection, avoiding your list be changed from outside the new ArrayList(list)
  5. Each collection should get wrapped in its own class, so now behaviors related to the collection have a home (e.g. filter methods, applying a rule to each element).

Miscellaneous

  1. Prefer lambdas to anonymous classes
  2. Prefer method references to lambdas
  3. Use enums instead of int constants.
  4. Avoid use float and double if exact answers are required, use BigDecimal instead, ex Money
  5. Prefer primitive types to boxed primitives
  6. The use of magic numbers in the code should be avoided. Use constants
  7. Don’t return Null. Communicate with your method client with `Optional`. The same for Collections — Return empty arrays or collections, not nulls
  8. Avoid creating unnecessary objects, reuse objects, and avoid unnecessary GC clean up

Lazy Initialization

Lazy initialization is a performance optimization. It’s used when data is deemed to be “expensive” for some reason. With Java 8 we should use the Supplier functional interface for that.

Java
 




x
18


 
1
== Thread safe Lazy initialization ===
2
public final class Lazy<T> {
3
 
4
    private volatile T value;
5
    public T getOrCompute(Supplier<T> supplier) {
6
        final T result = value; // Just one volatile read
7
        return result == null ? maybeCompute(supplier) : result;
8
    }
9
 
10
    private synchronized T maybeCompute(Supplier<T> supplier) {
11
        if (value == null) {
12
            value = supplier.get();
13
        }
14
        return value;
15
    }
16
}
17
Lazy<String> lazyToString= new Lazy<>()
18
return lazyToString.getOrCompute( () -> "(" + x + ", " + y + ")");



That's it for now, hope it was useful!

Java (programming language) code style Threading Interface (computing)

Opinions expressed by DZone contributors are their own.

Related

  • Deadlock-Free Synchronization in Java
  • How to Secure Apache Ignite From Scratch
  • Java Thread Synchronization and Concurrency Part 1
  • Advanced Brain-Computer Interfaces With Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!