文档章节

Best Practices for Exception Handling

Sally-wang
 Sally-wang
发布于 2015/10/21 16:13
字数 2283
阅读 5
收藏 0
点赞 0
评论 0

One of the problems with exception handling is knowing when and how to use it. In this article, I will cover some of the best practices for exception handling. I will also summarize  the recent debate about the use of checked exceptions.

We as programmers want to write quality code that solves problems. Unfortunately, exceptions come as side effects of our code. No one likes side effects, so we soon find our own ways to get around them. I have seen some smart programmers deal with exceptions the following way:

public void consumeAndForgetAllExceptions(){    try {        ...some code that throws exceptions    } catch (Exception ex){         ex.printStacktrace();    }}

What is wrong with the code above?

Once an exception is thrown, normal program execution is suspended and control is transferred to the catch block. The catch block catches the exception and just suppresses it. Execution of the program continues after the catch block, as if nothing had happened.

How about the following?

public void someMethod() throws Exception{}

This method is a blank one; it does not have any code in it. How can a blank method throw exceptions? Java does not stop you from doing this. Recently, I came across similar code where the method was declared to throw exceptions, but there was no code that actually generated that exception. When I asked the programmer, he replied "I know, it is corrupting the API, but I am used to doing it and it works."

Related Reading

Java Enterprise Best Practices
By The O'Reilly Java Authors


It took the C++ community several years to decide on how to use exceptions. This debate has just started in the Java community. I have seen several Java programmers struggle with the use of exceptions. If not used correctly, exceptions can slow down your program, as it takes memory and CPU power to create, throw, and catch exceptions. If overused, they make the code difficult to read and frustrating for the programmers using the API. We all know frustrations lead to hacks and code smells. The client code may circumvent the issue by just ignoring exceptions or throwing them, as in the previous two examples.

The Nature of Exceptions

Broadly speaking, there are three different situations that cause exceptions to be thrown:

  • Exceptions due to programming errors: In this category, exceptions are generated due to programming errors (e.g., NullPointerException and IllegalArgumentException). The client code usually cannot do anything about programming errors.

  • Exceptions due to client code errors: Client code attempts something not allowed by the API, and thereby violates its contract. The client can take some alternative course of action, if there is  useful information provided in the exception. For example: an exception is thrown while parsing an XML document that is not well-formed. The exception contains useful information about the location in the XML document that causes the problem. The client can use this information to take recovery steps.

  • Exceptions due to resource failures: Exceptions that get generated when resources fail.  For example: the system runs out of memory or a network connection fails. The client's response to resource failures is context-driven. The client can retry the operation after some time or just log the resource failure and bring the application to a halt.

Types of Exceptions in Java

Java defines two kinds of exceptions:

  • Checked exceptions: Exceptions that inherit from the Exception class are checked exceptions. Client code has to handle the checked exceptions thrown by the API, either in a catch clause or  by forwarding it outward with the throws clause.

  • Unchecked exceptions: RuntimeException also extends from Exception.  However, all of the exceptions that inherit from RuntimeException get special treatment. There is no requirement for the client code to deal with them, and hence they are called unchecked exceptions.

By way of example, Figure 1 shows the hierarchy for NullPointerException.

Figure 1
Figure 1. Sample exception hierarchy

In this diagram, NullPointerException extends from RuntimeException and hence is an unchecked exception.

I have seen heavy use of checked exceptions and minimal use of unchecked exceptions.  Recently, there has been a hot debate in the Java community regarding checked exceptions and their true value. The debate stems from fact that Java seems to be the first mainstream OO language with checked exceptions. C++ and C# do not have checked exceptions at all; all exceptions in these languages are unchecked.

A checked exception thrown by a lower layer is a forced contract on the invoking layer to catch or throw it. The checked exception contract between the API and its client soon changes into an unwanted burden if the client code is unable to deal with the exception effectively. Programmers of the client code may start taking shortcuts by suppressing the exception in an empty catch block or just throwing it and, in effect, placing the burden on the client's invoker.

Checked exceptions are also accused of breaking encapsulation. Consider the following:

public List getAllAccounts() throws    FileNotFoundException, SQLException{    ...}

The method getAllAccounts() throws two checked exceptions. The client of this method  has to explicitly deal with the implementation-specific exceptions, even if it has no idea what file or database call hasfailed within getAllAccounts(), or has no business providing filesystem or database logic.  Thus, the exception handling forces an inappropriately tight coupling between the method and its callers.

Best Practices for Designing the API

Having said all of this, let us now talk about how to design an API that throws exceptions properly.

1. When deciding on checked exceptions vs. unchecked exceptions, ask yourself, "What action can the client code take when the exception occurs?"

If the client can take some alternate action to recover from the exception, make it a checked exception. If the client cannot do anything useful, then make the exception unchecked. By useful, I mean taking steps to recover from the exception and not just logging the exception. To summarize:

Client's reaction when exception happens Exception type
Client code cannot do anything Make it an unchecked exception
Client code will take some useful recovery action based on information in exception Make it a checked exception

Moreover, prefer unchecked exceptions for all programming errors: unchecked exceptions have the benefit of not forcing the client API to explicitly deal with them. They propagate to where you want to catch them, or they go all the way out and get reported. The Java API has many unchecked exceptions, such as NullPointerException, IllegalArgumentException, and IllegalStateException. I prefer working with standard exceptions provided in Java rather than creating my own. They make my code easy to understand and avoid increasing the memory footprint of code.

2. Preserve encapsulation.

Never let implementation-specific checked exceptions escalate to the higher layers. For example, do not propagate SQLException from data access code to the business objects layer. Business objects layer do not need to  know about SQLException. You have two options:

  • Convert SQLException into another checked exception, if the client code is expected to recuperate from the exception.

  • Convert SQLException into an unchecked exception, if the client code cannot do anything about it.

Most of the time, client code cannot do anything about SQLExceptions. Do not hesitate to convert them into unchecked exceptions. Consider the following piece of code:

public void dataAccessCode(){    try{        ..some code that throws SQLException    }catch(SQLException ex){        ex.printStacktrace();   }}

This catch block just suppresses the exception and does nothing. The justification is that there is nothing my client could do about an SQLException. How about dealing with it in the following manner?

public void dataAccessCode(){    try{        ..some code that throws SQLException    }catch(SQLException ex){        throw new RuntimeException(ex);    }}

This converts SQLException  to RuntimeException. If SQLException occurs, the catch clause throws a new RuntimeException. The execution thread is suspended and the exception gets reported. However, I am not corrupting my business object layer with unnecessary exception handling, especially since it cannot do anything about an SQLException. If my catch needs the root exception cause, I can make use of the getCause() method available in all exception classes as of JDK1.4.

If you are confident that the business layer can take some recovery action when SQLException occurs, you can convert it into a more meaningful checked exception. But I have found that just throwing RuntimeException suffices most of the time.

3. Try not to create new custom exceptions if they do not have useful information for client code.

What is wrong with following code?

public class DuplicateUsernameException    extends Exception {}

It is not giving any useful information to the client code, other than an indicative exception name. Do not forget that Java Exception classes are like other classes, wherein you can add methods that you think the client code will invoke to get more information.

We could add useful methods to DuplicateUsernameException, such as:

public class DuplicateUsernameException    extends Exception {    public DuplicateUsernameException         (String username){....}    public String requestedUsername(){...}    public String[] availableNames(){...}}

The new version provides two useful methods: requestedUsername(), which returns the requested name, and availableNames(), which returns an array  of available usernames similar to the one requested. The client could use these methods to inform that the requested username is not available and that other usernames are available. But if you are not going to add extra information, then just throw a standard  exception:

throw new Exception("Username already taken");

Even better, if you think the client code is not going to take any action other than logging if the username is already taken, throw a unchecked exception:

throw new RuntimeException("Username already taken");

Alternatively, you can even provide a method that checks if the username is already taken.

It is worth repeating that checked exceptions are to be used in situations where the client API can take some productive action based on the information in the exception. Prefer unchecked exceptions for all programmatic errors. They make  your code more readable.

4. Document exceptions.

You can use Javadoc's @throws tag to document both checked and unchecked exceptions that your API throws. However, I prefer to write unit tests to document exceptions. Tests allow me to see the exceptions in action and hence serve as documentation that can be executed. Whatever you do, have some way by which the client code can learn of the exceptions that your API throws. Here is a sample unit test that tests for IndexOutOfBoundsException:

public void testIndexOutOfBoundsException() {    ArrayList blankList = new ArrayList();    try {        blankList.get(10);        fail("Should raise an IndexOutOfBoundsException");    } catch (IndexOutOfBoundsException success) {}}

The code above should throw an IndexOutOfBoundsException when blankList.get(10) is invoked. If it does not, the fail("Should raise an IndexOutOfBoundsException") statement explicitly fails the test. By writing unit tests for exceptions, you not only document how the exceptions work, but also make your code robust by testing for exceptional scenarios.

Best Practices for Using Exceptions

The next set of best practices show how the client code should deal with an API that throws checked exceptions.

1. Always clean up after yourself

If you are using resources like database connections or network connections, make sure you clean them up. If the API you are invoking uses only unchecked exceptions, you should still clean up resources after use, with try - finally blocks.

public void dataAccessCode(){    Connection conn = null;    try{        conn = getConnection();        ..some code that throws SQLException     }catch(SQLException ex){        ex.printStacktrace();    } finally{       DBUtil.closeConnection(conn);    }}class DBUtil{    public static void closeConnection        (Connection conn){        try{             conn.close();        } catch(SQLException ex){             logger.error("Cannot close connection");            throw new RuntimeException(ex);        }    }}

DBUtil is a utility class that closes the Connection. The important point is the use of finally block,which executes whether or not an exception is caught.  In this example, the finally closes the connection and throws a RuntimeExceptionif there is problem with closing the connection.

2. Never use exceptions for flow control

Generating stack traces is expensive and the value of a stack trace is in debugging.  In a flow-control situation,    the stack trace would be ignored, since the client just wants to know how to proceed.

In the code below, a custom exception, MaximumCountReachedException, is used to control the flow.

public void useExceptionsForFlowControl() {    try {        while (true) {            increaseCount();        }    } catch (MaximumCountReachedException ex) {    }    //Continue execution}public void increaseCount()    throws MaximumCountReachedException {    if (count >= 5000)        throw new MaximumCountReachedException();}

The useExceptionsForFlowControl() uses an infinite loop to increase the count until the exception is thrown. This not only makes the code difficult to read, but also makes it slower. Use exception handling only in exceptional situations.

3. Do not suppress or ignore exceptions

When a method from an API throws a checked exception, it is trying to tell you that you should take some counter action. If the checked exception does not make sense to you, do not hesitate to convert it into an unchecked exception and throw it again, but do not ignore it by catching it with {} and then continueas if nothing had happened.

4. Do not catch top-level exceptions

Unchecked exceptions inherit from the RuntimeException class, which in turn inherits from Exception. By catching the Exception class, you are also catching RuntimeException as in the following code:

try{..}catch(Exception ex){}

The code above ignores unchecked exceptions, as well.

5. Log exceptions just once

Logging the same exception stack trace more than once can confuse the programmer examining the stack trace about the original source of exception. So just log it once.

Summary

These are some suggestions for exception-handling best practices. I have no intention of staring a religious war on checked exceptions vs. unchecked exceptions. You will have to customize the design and usage according to your requirements. I am confident that over time, we will find better ways to code with exceptions.

I would like to thank Bruce Eckel, Joshua Kerievsky, and Somik Raha for their support in writing this article.

 reprint from http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=1


本文转载自:

共有 人打赏支持
Sally-wang
粉丝 2
博文 2
码字总数 455
作品 0
朝阳
高级程序员
Java自定义Exception

这里总结一下Java的Exception,并实现一个自定义Exception类。 总结: Throwable对象分为两种类型:Error(表示编译时和系统错误);Exception(在Java类库、用户方法以及运行时故障中抛出的异常...

Triangle23 ⋅ 2014/12/02 ⋅ 1

WebAPI 相关资料

Intruduction Getting Started with ASP.NET Web API 2 系列文章——Web API从开始到结束 Demos Web API Thoughts 1 of 3 - Data Streaming MVC AngularJS Master/Detail CRUD, Filter And ......

东风125 ⋅ 2015/11/30 ⋅ 0

Node.js 3.2.0 发布

io.js 3.2.0 发布,此版本值得关注的改进如下: events: Added as a replacement for , which has now been marked as deprecated in the docs. (Sakthipriyan Vairamani) #2349 module: Fix......

oschina ⋅ 2015/08/26 ⋅ 25

Windows: MSDN资源

Windows 7 and Windows Server 2008 R2 Application Quality Cookbook : http://msdn.microsoft.com/en-us/library/dd371778%28VS.85%29.aspx 其中: Tools, Best Practices, and Guidance T......

junwong ⋅ 2012/03/09 ⋅ 0

Rails best practices

Rails best practices 主页:http://rails-bestpractices.com/ github:https://github.com/flyerhzm/railsbestpractices 安装: ruby1.9+rails31 1. gem install railsbestpractices 2. Gem......

阿昭 ⋅ 2011/10/21 ⋅ 0

java异常处理基本原则

总原则:”Throw early catch late" principle This is probably the most famous principle about Exception handling. It basically says that you should throw an exception as soon as ......

xixicat ⋅ 2014/02/21 ⋅ 0

JDBC十佳实践

Java程序员的JDBC十佳实践 Java JDBC 最佳实践 JDBC Best Practices are some coding practices which Java programmer should follow while writing JDBC code. As discussed in how to co......

YuanyuanL ⋅ 2014/09/18 ⋅ 0

10+ Useful Javascript Libraries for Your New Proje

JavaScript Library is basically a pre-written scripting language that ease the development of JavaScript based applications such as AJAX and other web-centric technologies.These......

linjine ⋅ 2015/05/05 ⋅ 0

50 Tips to Boost ASP.NET Performance

When we are looking to optimize the performance of web applications we should keep in mind about Memory Load, Processor Load and Network Bandwidth. Here are 50 best practices to......

iiiiiiiii ⋅ 2011/01/04 ⋅ 0

来自Swift社区的最佳实践

https://github.com/KevinHM/ios-good-practices-the-lastest-version/blob/master/Swift-Best-Practices.md 来自 Swift 社区的最佳实践,主题还在持续更新中。。。...

KevinHM ⋅ 2015/09/20 ⋅ 1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 57分钟前 ⋅ 0

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 今天 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 今天 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 今天 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 今天 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部