Master Java Exception Handling: 9 Proven Best Practices
This article explains nine essential best‑practice guidelines for handling exceptions in Java, covering resource cleanup, specific exception types, documentation, informative messages, catch ordering, avoiding Throwable, proper logging, and exception wrapping to improve code readability and reliability.
Handling exceptions in Java is not a simple task. Even experienced developers spend considerable time deciding which exceptions to handle and how to handle them. Most development teams define rules for exception handling, and these rules often differ between teams.
This article presents several best practices for exception handling that are widely used by many teams.
1. Clean up resources in a finally block or use try‑with‑resources
A common mistake is to close resources such as InputStream at the end of the try block.
public void doNotCloseResourceInTry() {<br/> FileInputStream inputStream = null;<br/> try {<br/> File file = new File("./tmp.txt");<br/> inputStream = new FileInputStream(file);<br/> // use the inputStream to read a file<br/> // do NOT do this<br/> inputStream.close();<br/> } catch (FileNotFoundException e) {<br/> log.error(e);<br/> } catch (IOException e) {<br/> log.error(e);<br/> }<br/>}<br/>If an exception is thrown before the explicit close, the resource will not be released. The proper approach is to place cleanup code in a finally block or use the try‑with‑resources statement.
public void closeResourceInFinally() {<br/> FileInputStream inputStream = null;<br/> try {<br/> File file = new File("./tmp.txt");<br/> inputStream = new FileInputStream(file);<br/> // use the inputStream to read a file<br/> } catch (FileNotFoundException e) {<br/> log.error(e);<br/> } finally {<br/> if (inputStream != null) {<br/> try {<br/> inputStream.close();<br/> } catch (IOException e) {<br/> log.error(e);<br/> }<br/> }<br/> }<br/>}<br/><br/>public void automaticallyCloseResource() {<br/> File file = new File("./tmp.txt");<br/> try (FileInputStream inputStream = new FileInputStream(file)) {<br/> // use the inputStream to read a file<br/> } catch (FileNotFoundException e) {<br/> log.error(e);<br/> } catch (IOException e) {<br/> log.error(e);<br/> }<br/>}<br/>2. Declare the most specific exception
Use the most specific exception type in method signatures to make the code easier to understand.
public void doNotDoThis() throws Exception {<br/> ...<br/>}<br/>public void doThis() throws NumberFormatException {<br/> ...<br/>}<br/>For example, NumberFormatException clearly indicates a numeric format error.
3. Document thrown exceptions
When a method declares that it throws an exception, add Javadoc comments describing the circumstances.
/**<br/> * Performs a useful operation.<br/> *<br/> * @param input the input value<br/> * @throws MyBusinessException if a business rule is violated<br/> */<br/>public void doSomething(String input) throws MyBusinessException {<br/> ...<br/>}<br/>4. Include descriptive messages when throwing exceptions
Provide concise, precise messages that help readers and monitoring tools understand the problem.
try {<br/> new Long("xyz");<br/>} catch (NumberFormatException e) {<br/> log.error(e);<br/>}<br/>If the exception name is not self‑explanatory, add a short message with relevant details.
5. Catch the most specific exceptions first
Catch blocks are evaluated in order; the first matching block handles the exception. Therefore, catch specific exceptions before more general ones.
public void catchMostSpecificExceptionFirst() {<br/> try {<br/> doSomething("A message");<br/> } catch (NumberFormatException e) {<br/> log.error(e);<br/> } catch (IllegalArgumentException e) {<br/> log.error(e);<br/> }<br/>}<br/>6. Never catch Throwable
Throwableis the superclass of all errors and exceptions. Catching it also captures unrecoverable JVM errors, which should generally be left uncaught.
public void doNotCatchThrowable() {<br/> try {<br/> // do something<br/> } catch (Throwable t) {<br/> // don't do this!<br/> }<br/>}<br/>7. Do not ignore caught exceptions
Never write an empty catch block or ignore the exception; always log or handle it so that unexpected issues can be diagnosed.
public void logAnException() {<br/> try {<br/> // do something<br/> } catch (NumberFormatException e) {<br/> log.error("This should never happen: " + e);<br/> }<br/>}<br/>8. Avoid logging and re‑throwing the same exception
Logging an exception and then re‑throwing it results in duplicate log entries. Instead, either let the exception propagate or wrap it in a custom exception with additional context.
public void wrapException(String input) throws MyBusinessException {<br/> try {<br/> // do something<br/> } catch (NumberFormatException e) {<br/> throw new MyBusinessException("A message that describes the error.", e);<br/> }<br/>}<br/>9. Preserve the original exception when wrapping
When converting a standard exception to a custom one, always pass the original exception as the cause so that stack traces remain intact.
public void wrapException(String input) throws MyBusinessException {<br/> try {<br/> // do something<br/> } catch (NumberFormatException e) {<br/> throw new MyBusinessException("A message that describes the error.", e);<br/> }<br/>}<br/>Conclusion
When throwing or catching exceptions, many considerations affect code readability and API usability. Exceptions serve as a communication medium; discussing these best practices and establishing team conventions 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.
