9 Best Practices for Java to handle exceptions!

Keywords: Programming Java Spring jvm Maven

Handling exceptions in Java is not an easy task.

It is not only difficult for beginners to understand, even some experienced developers also need to spend a lot of time thinking about how to handle exceptions, including which exceptions need to be handled, how to handle and so on.

This is also the reason why most development teams will make some rules to regulate the handling of exceptions. The norms between teams are often quite different.

This article presents several best practices for exception handling that are used by many teams.

1. Clean up resources in the Finally block or use try with resource statement

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

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 runs without any exception. However, when the statement in the try block throws an exception or the code of its own implementation throws an exception, the final closing statement will not be executed and the resource cannot be released.

A reasonable way to do this is to put all cleaned up code in a finally block or use a try with resource statement. Recommended reading: 10 abhorrent anomalies . WeChat official account: Java technology stack, back in the background: Java, can get my N Java tutorial, all dry cargo.

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);
    }
}

2. Specify specific exceptions

Use the most specific exception as much as possible to declare the method, so that the code is easier to understand.

public void doNotDoThis() throws Exception {
    ...
}
public void doThis() throws NumberFormatException {
    ...
}

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

3. Document exceptions

Documentation is also required when declaring throw exceptions on methods. As before, it is to provide the caller with as much information as possible, so as to avoid / handle exceptions better. 10 best practices for exception handling , this one is also recommended.

Add the throws declaration in Javadoc and describe the scenario of throwing exceptions.

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

4. include description information when throwing an exception

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

But this is not to say that we need to make a long speech about the error information, because the class name of Exception can reflect the cause of the error, so we only need to use one or two sentences to describe it.

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

NumberFormatException tells us that the exception is a format error. Additional information about the exception only needs to provide the error string. When the name of the exception is not obvious enough, you need to provide as specific error information as possible.

5. First catch the most specific exception

Now many ides can intelligently prompt this best practice. When you try to catch the most general exception first, you will be prompted with * * unreachable code * *.

When there are multiple catch blocks, only the first matched catch block can be executed according to the capture order. Therefore, if the IllegalArgumentException is caught first, the capture of NumberFormatException cannot be run.

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 of all exceptions and errors. You can catch in a catch statement, but never.

If you catch throwable, you will not only catch all exception s, but also catch errors. Error is a jvm error indicating that it cannot be recovered. Therefore, do not capture throwable unless you are absolutely certain that you can handle or are required to handle errors.

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 exceptions, so they write a catch block, but they do not do any processing or log.

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

But the reality is that there are often unexpected exceptions or it is uncertain whether the code here will change in the future (the code that prevents the exception from being thrown is deleted). At this time, because the exception is caught, it is impossible to get enough error information to locate the problem.

It is reasonable to record at least abnormal information.

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

8. Do not record and throw exceptions

It can be found that many codes and even class libraries have logic to catch exceptions, record logs and throw them again. As follows:

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

This processing logic seems reasonable. However, it often outputs multiple logs to the same exception. As follows:

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 later logs. If you want to provide more useful information, you can wrap exceptions as custom exceptions.

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

Therefore, catch only when you want to handle exceptions, otherwise you only need to declare in the method signature for the caller to handle.

9. Do not discard the original exception when the package is abnormal

It is a common practice to catch standard exceptions and wrap them as custom exceptions. In this way, more specific exception information can be added and targeted exception handling can be done.

It should be noted that when wrapping an exception, you must set the original exception to cause(Exception has a constructor that can be passed into cause). Otherwise, losing the original exception information will make error analysis difficult.

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

summary

To sum up, there are many different things to consider when throwing or catching exceptions. Many of these points are to improve the readability of the code or the usability of the api.

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

Original text: https://dzone.com/articles/9-best-practices-to-handle-exceptions-in-java Translator: SA ran Hang Translation: http://www.rowkey.me/blog/2017/09/17/java-exception/

Recommend to my blog to read more:

1.Java JVM, collection, multithreading, new features series

2.Spring MVC, Spring Boot, Spring Cloud series tutorials

3.Maven, Git, Eclipse, Intellij IDEA series tools tutorial

4.Latest interview questions of Java, backend, architecture, Alibaba and other large factories

Feel good, don't forget to like + forward!

Posted by vonnero on Thu, 14 May 2020 01:31:20 -0700