Handling Exceptions in Java is not a simple matter. Not only is it difficult for beginners to understand, even some experienced developers spend a lot of time thinking about how to handle exceptions, including what exceptions to handle, how to handle them, and so on. This is the reason most development teams develop rules that govern the handling of exceptions. These specifications are often quite different between the teams.
This article gives several exception handling best practices that are used by many teams.
1. Clean up resources in the finally block or use the Try-with-resource statement
When using a resource similar to inputstream that needs to be closed after use, a common mistake is to close the resource at the end of the try block.
1234567891011121314 |
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 is not a problem to run without any exception. However, when the statement in the try block throws an exception or the code that implements it itself throws an exception, the last closing statement is not executed and the resource is not freed.
It is reasonable to put all the cleaned code in the finally block or use the Try-with-resource statement.
12345< Span class= "Line-number" >6789< Span class= "Line-number" >10111213 141516 17181920 212223 24252627 2829 |
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.E Rror (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 (IOE Xception e) { log.error (e); } }
|
2. Specify the specific exception
Use the most specific exceptions as much as possible to declare the method, which makes the code easier to understand.
123456 |
public void doNotDoThis() throws Exception { ...}public void doThis() throws NumberFormatException { ...}
|
As above, NumberFormatException literally can be seen as a numeric formatting error.
3. Documentation of exceptions
When declaring an exception on a method, a document description is also required. As in the previous point, it is all about providing the caller with as much information as possible to better avoid/handle exceptions.
Add the throws declaration to Javadoc and describe the scenario that throws the exception.
123456789 |
/** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */public void doSomething(String input) throws MyBusinessException { ...}
|
4. Include descriptive information when throwing an exception
When you throw an exception, you need to describe the problem and related information as precisely as possible, so that you can read it more easily, either in the print to the log or in the monitoring tool, to better locate the specific error message, the severity of the error, and so on.
But this is not to say that you want to make a lengthy statement about the error message, because the exception class name can reflect the wrong reason, so you only need one or two words to describe it.
123)45 |
try { new Long("xyz");} catch (NumberFormatException e) { log.error(e);}
|
NumberFormatException tells you that the exception is a format error, and that the extra information for the exception only needs to provide the error string. When the name of the exception is not obvious enough, you need to provide as much specific error information as possible.
5. First capture the most specific exception
Many IDES now intelligently hint at this best practice, and when you try to catch the most general exceptions first, you will be prompted for code that cannot be reached .
When there are multiple catch blocks, only the first matching catch block can be executed according to the capture order. Therefore, if you capture illegalargumentexception first, you cannot run to a capture of numberformatexception.
123456789 |
public void catchMostSpecificExceptionFirst() { try { doSomething("A message"); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) }}
|
6. Do not capture Throwable
Throwable is the parent class for all exceptions and errors. You can capture it in a catch statement, but never do so.
If you catch throwable, you will not only catch all exception, but also catch the error. Error is a JVM error that indicates an unrecoverable failure. So unless you're absolutely sure you can handle or be asked to handle the error, don't capture Throwable.
1234567 |
public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // don‘t do this! }}
|
7. Do not ignore exceptions
Many times, developers are confident that they will not throw an exception, so they write a catch block, but do not do any processing or logging.
12345678 |
public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen }}
|
But the reality is that there are often unpredictable anomalies or the inability to determine whether the code in the future will change (removing the code that prevents the exception from throwing), and because the exception is caught, so that it cannot get enough error information to locate the problem.
It is reasonable to record at least the information of the exception.
1234567 |
public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error("This should never happen: " + e); }}
|
8. Do not record and throw exceptions
You can see that many code and even the class library have the logic to catch exceptions, log logs, and throw them again. As follows:
123456 |
try { new Long("xyz");} catch (NumberFormatException e) { log.error(e); throw e;}
|
This processing logic is reasonable to look at. However, this often causes multiple logs to be output for the same exception. As follows:
1234567 |
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Long.parseLong(Long.java:589)at java.lang.Long.(Long.java:965)at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
|
As shown above, there is no more useful information attached to the subsequent logs. If you want to provide more useful information, you can wrap the exception as a custom exception.
12345678 |
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); }}
|
Therefore, only when you want to handle the exception to capture, otherwise only need to declare in the method signature let the caller to handle.
9. Do not discard the original exception when packing the exception
Capturing standard exceptions and wrapping them as custom exceptions is a common practice. This allows you to add more specific exception information and be able to do the exception handling that is targeted.
It is important to note that the original exception must be set to cause when the wrapper is abnormal (exception has a construction method to pass in cause). Otherwise, the loss of the original exception information makes it difficult to parse the error.
1234567 |
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); }}
|
Summarize
In conclusion, there are a lot of different things to consider when throwing or catching exceptions. Many of these points are designed to improve the readability of the code or the usability of the API.
Exceptions are not just an error-control mechanism, but also a communication medium, so discussing these best practices with your collaborators and developing specifications will allow everyone to understand the relevant generic concepts and be able to use them in the same way.
Ext.: http://www.rowkey.me/blog/2017/09/17/java-exception/
Java-exception Practice