Master Java Exception Handling: 10 Best Practices Every Developer Should Follow
This article presents ten essential Java exception‑handling best practices—including proper resource cleanup, using specific exception types, documenting throws clauses, providing clear messages, catching the most specific exceptions first, avoiding Throwable, logging wisely, and preserving original causes—to improve code readability, reliability, and maintainability.
Handling exceptions in Java is not a trivial task; even experienced developers often spend considerable time deciding which exceptions to handle and how.
Most development teams establish their own rules for exception handling, and these rules can vary widely.
This article outlines several best‑practice guidelines widely adopted by many teams.
Clean up resources in a finally block or use try‑with‑resources
A common mistake is closing resources like InputStream at the end of the try block, which prevents the close operation from running if an exception occurs.
public void doNotCloseResourceInTry() {
FileInputStream inputStream = null;
try {
File file = new File("./tmp.txt");
inputStream = new FileInputStream(file);
// use the inputStream to read a file
// do NOT do this
inputStream.close();
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
}The above code works only when no exception is thrown. If an exception occurs, the close statement is skipped and the resource is not released.
The proper approach is to place cleanup code in a finally block or to use the try‑with‑resources statement.
public void closeResourceInFinally() {
FileInputStream inputStream = null;
try {
File file = new File("./tmp.txt");
inputStream = new FileInputStream(file);
// use the inputStream to read a file
} catch (FileNotFoundException e) {
log.error(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error(e);
}
}
}
}
public void automaticallyCloseResource() {
File file = new File("./tmp.txt");
try (FileInputStream inputStream = new FileInputStream(file)) {
// use the inputStream to read a file
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
}Declare the most specific exception
Prefer the most specific exception type in method signatures to make the code easier to understand.
public void doNotDoThis() throws Exception {
...
}
public void doThis() throws NumberFormatException {
...
}For example, NumberFormatException clearly indicates a numeric format error.
Document exceptions in Javadoc
When a method declares that it throws an exception, add a Javadoc @throws entry describing the circumstances.
/**
* This method does something extremely useful ...
*
* @param input
* @throws MyBusinessException if ... happens
*/
public void doSomething(String input) throws MyBusinessException {
...
}Include descriptive information when throwing
Provide a concise description (one or two sentences) that clarifies the problem, even though the exception class name already conveys the basic reason.
try {
new Long("xyz");
} catch (NumberFormatException e) {
log.error(e);
}If the exception name is not self‑explanatory, add a specific message.
Catch the most specific exception first
Catching a broad exception before a specific one prevents the specific handler from ever executing.
public void catchMostSpecificExceptionFirst() {
try {
doSomething("A message");
} catch (NumberFormatException e) {
log.error(e);
} catch (IllegalArgumentException e) {
log.error(e);
}
}Never catch Throwable
Throwableincludes both exceptions and errors; catching it can swallow unrecoverable JVM errors. Only catch it if you are absolutely sure you can handle every possible error.
public void doNotCatchThrowable() {
try {
// do something
} catch (Throwable t) {
// don't do this!
}
}Do not ignore exceptions
Never leave a catch block empty or without logging; unexpected exceptions can surface later and make debugging difficult. At a minimum, record the exception.
public void logAnException() {
try {
// do something
} catch (NumberFormatException e) {
log.error("This should never happen: " + e);
}
}Do not log and rethrow the same exception
Logging an exception and then rethrowing it leads to duplicate log entries. Instead, either let the caller handle it or wrap it in a custom exception.
try {
new Long("xyz");
} catch (NumberFormatException e) {
log.error(e);
throw e;
}When wrapping, preserve the original cause
Wrap standard exceptions in custom ones to add context, but always pass the original exception as the cause so that stack traces remain complete.
public void wrapException(String input) throws MyBusinessException {
try {
// do something
} catch (NumberFormatException e) {
throw new MyBusinessException("A message that describes the error.", e);
}
}Conclusion
When throwing or catching exceptions, many considerations affect code readability and API usability. Treat exceptions not only as an error‑control mechanism but also as a communication medium; discussing these best practices and establishing team guidelines helps everyone understand and apply them consistently.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
