Clean up the resource in the Finally block or use the try-with-resource statement

When using a resource like InputStream that needs to be closed after use, a common mistake is to close the resource at the end of the try block.

public void doNotCloseResourceInTry(a) {
    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); }}Copy the code

The above code runs fine without any exceptions. But when the statement in the try block throws an exception or the code you implement throws an exception, the final closing statement is not executed and the resource is not freed.

It makes sense to put all the cleanup code ina finally block or to use a try-with-resource statement.

public void closeResourceInFinally(a) {
    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(a) {
    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); }}Copy the code

Specify the specific exception

Use the most specific exceptions possible to declare methods so that the code is easier to understand.

public void doNotDoThis(a) throws Exception {... }public void doThis(a) throws NumberFormatException {... }Copy the code

As mentioned above, NumberFormatException is literally a number formatting error.

Document the exceptions

When an exception is declared on a method, it also needs to be documented. As with the previous point, the goal is to give the caller as much information as possible so that exceptions can be better avoided/handled. This article also recommends 10 best practices for exception handling.

Add a throws declaration to Javadoc, and describe the scenarios in which exceptions are thrown.

/**
 * This method does something extremely useful ...
 *
 * @param input
 * @throws MyBusinessException if ... happens
 */
public void doSomething(String input) throws MyBusinessException {... }Copy the code

Contains description information when an exception is thrown

When throwing an exception, you need to describe the problem and related information as accurately as possible so that it can be more easily read, whether printed in a log or in a monitoring tool, to better locate the specific error message, the severity of the error, and so on.

I don’t mean to be verbose about the error message, though, because the Exception class name tells the reason for the error, so it only needs one or two sentences.

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
}
Copy the code

NumberFormatException tells you that the exception is a formatting error. The additional information for the exception is simply to provide the error string. When the name of the exception is not obvious, you need to provide the error information as specific as possible.

Catch the most specific exceptions first

Many current ides have smart hints as a best practice, suggesting unreachable code when you try to catch the most general exception first. When there are more than one catch block, only the first catch block in catch order can be executed. Therefore, if the IllegalArgumentException is caught first, then the catch of NumberFormatException cannot be run.

public void catchMostSpecificExceptionFirst(a) {
    try {
        doSomething("A message");
    } catch (NumberFormatException e) {
        log.error(e);
    } catch (IllegalArgumentException e) {
        log.error(e)
    }
}
Copy the code

Don’t capture Throwable

Throwable is the parent class of all exceptions and errors. You can catch in a catch statement, but never do so. If throwable is caught, not only will all exceptions be caught, but errors will also be caught. An ERROR is an unrecoverable JVM error. So do not catch throwable unless you are absolutely certain that you can handle or are required to handle an error.

public void doNotCatchThrowable(a) {
    try {
        // do something
    } catch (Throwable t) {
        // don't do this!}}Copy the code

Don’t ignore exceptions

Many times, developers are confident that an exception will not be thrown, so they write a catch block, but do nothing to it or log it.

public void doNotIgnoreExceptions(a) {
    try {
        // do something
    } catch (NumberFormatException e) {
        // this will never happen}}Copy the code

But the reality is that often there will be an unexpected exception or no certainty that the code will change in the future (the code that prevents the exception from being thrown is removed), and because the exception is caught, there is not enough error information to locate the problem. It is reasonable to log at least the exception information.

public void logAnException(a) {
    try {
        // do something
    } catch (NumberFormatException e) {
        log.error("This should never happen: "+ e); }}Copy the code

Do not log and throw exceptions

You’ll find a lot of code and even class libraries that have logic to catch an exception, log it, and throw it again. As follows:

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
    throw e;
}
Copy the code

The processing logic seems reasonable. However, this often produces multiple logs for the same exception.

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e); }}Copy the code

Therefore, only catch an exception if you want to handle it; otherwise, just declare it in the method signature for the caller to handle

Do not discard the original exception when wrapping it

It is a common practice to catch standard exceptions and wrap them as custom ones. This allows you to add more specific exception information and to do specific exception handling.

When wrapping an Exception, make sure the original Exception is set to cause(exceptions have a constructor that can pass in cause). Otherwise, the loss of the original exception information makes it difficult to analyze the error.

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e); }}Copy the code

conclusion

In summary, there are many different things to consider when throwing or catching exceptions. Many of these points are aimed at improving the readability of the code or the availability of the API.

Exceptions are not just an error-control mechanism, they are also a communication medium, so discussing these best practices with your collaborators and developing some specifications allows everyone to understand the relevant common concepts and use them in the same way.

Source | ww7. Rowkey. Me /