Three suggestions for handling exceptions given by Java tycoon

Three suggestions for handling exceptions given by Java tycoon

Origin: I happened to see the blog written by foreigners. After reading it, I think it's good. Share it. PS: Original address

Oh no, don't do this to me...

Chinese translation:

Oh, please don't write like this

// First: write a comment to skip the exception
try {
    throw new IOException("Made up");
} catch (IOException e) {
    // skip
}
// Second: record in the log and continue processing
try {
    throw new IOException("Made up");
} catch (IOException e) {
    log.error("blah blah blah", e);
}
// Third: Mark TODO and do nothing
try {
    throw new IOException("Made up");
} catch (IOException e) {
    // TODO - handle exception (;
}

... and still, I am finding those catch blocks inside various projects. This is a great way to suppress the problem — for a short period of time. A few weeks or months later, this will become a nightmare for developers. Walking through the old log files and trying to figure out what went wrong is definitely not that most people want to do. Therefore, let's avoid that.

I found this catch statement in various projects. This is a "good way" to cover up problems in the short term. In a few weeks or months, however, the code will become a developer's nightmare. Most people don't want to read the log to look up problems. So let's avoid that.

The first rule is that catch blocks are here to handle exceptional situations. Logging exceptions and moving on is not considered as handling. The only case when it makes sense to suppress exception is during the closing of the resource after a prior exception (this case isn't covered within this article; if you are interested, then here is an old yet still very good blog post written by McDowell).

Rule 1: the catch statement is used to handle exceptions. Record the exceptions in the log and continue to execute without processing. The only exception is to shut down the resource after an exception occurs (this article does not discuss this situation; if you are interested, you can refer to this McDowell blog, which has a good content although it was written earlier).

There are three basic patterns of how to handle exceptions: translate, retry, and recover.

There are three basic patterns for handling exceptions: translate, retry, and recover.

Translate is often used when you have to deal with a checked exception, your method can't bubble it up, and recovery is not possible. In such cases, it is appropriate to translate it into a runtime exception and throw up. Then, the runtime exception is typically handled in the framework level. Retry is useful when dealing with unreliable services. It makes sense only if retrying makes sense fundamentally. The good example is retrying to overcome network interruptions. Recover is good when the strategy for doing that is defined. For example, you can write data to the local storage if sending over network fails. Of course, then it's necessary to define what to do with that file.

Transformations are often used to handle checked exception s, when exceptions in methods cannot be thrown and cannot be recovered. In this case, it is most appropriate to convert it to a runtime exception and then throw it. Next, runtime exceptions are usually handled by the framework. When dealing with unreliable services, retrying is useful if it makes sense to try again. A good example is network interrupt retry. If you define this policy, you can return to normal. For example, if sending data over the network fails, you can write the data to local storage. At this point, of course, you have to define how to handle the file.

In addition, the mentioned patterns might be combined. Examples are as follows.

In addition, the patterns mentioned above can be combined, such as the following example.

// Translate (transform)
try {
    thrownew IOException("Made up");
} catch (IOException e) {
    thrownew RuntimeException(e);
}
// retry(5 times)
boolean end = false;
int count = 0;
while (end == false) {
    try {
        // Send message
        if (true) {
            thrownew MessagingException("Made up");
        }
        end = true;
    } catch (MessagingException e) {
        if (count >= 5) {
            // Try to give up 5 times.
            thrownew RuntimeException("was not able to send message even after five tries", e);
        }
        ++count;
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e1) {
            Thread.currentThread().interrupt();
            thrownew RuntimeException(e1);
        }
    }
}
// recover, if the transfer fails, record to the file
try {
    // Send message
    thrownew MessagingException("Made up");
} catch (MessagingException e) {
    try {
        // Writing file
        thrownew IOException("Made up");
    } catch (IOException e1) {
        // If the write file fails, the recovery will not be performed
        thrownew RuntimeException(e1);
    }
}

If everything fails, then this way will at least guarantee that you will be aware of the problem. In addition, it will provide you always the true cause of the issue; therefore, you will be able to quickly identify where the problem is!

If everything fails, at least this approach ensures that you are aware of the problem. In addition, it provides the real cause of the problem so that you can quickly locate the problem.

Happy handling!

Happy programming!

Published 1 original article, praised 0, visited 7
Private letter follow

Tags: network Java Programming

Posted on Mon, 13 Jan 2020 07:28:56 -0500 by Bizzle