1. 当要决定是采用checked exception还是unchecked exception的时候,你要问自己一个问题,“如果这种异常一旦抛出,客户端会做怎样的补救?”
[原文:when deciding on checked exceptions vs. unchecked exceptions, ask yourself, "what action can the client code take when the exception occurs "]
如果客户端可以通过其他的方法恢复异常,那么这种异常就是checked exception;如果客户端对出现的这种异常无能为力,那么这种异常就是unchecked exception;从使用上讲,当异常出现的时候要做一些试图恢复它的动作而不要仅仅的打印它的信息,总来的来说,看下表:
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
此外,尽量使用unchecked exception来处理编程错误:因为unchecked exception不用使客户端代码显示的处理它们,它们自己会在出现的地方挂起程序并打印出异常信息。java api中提供了丰富的unchecked excetpion,譬如:nullpointerexception , illegalargumentexception 和 illegalstateexception等,因此我一般使用这些标准的异常类而不愿亲自创建新的异常类,这样使我的代码易于理解并避免的过多的消耗内存。
2. 保护封装性(preserve encapsulation)
不要让你要抛出的checked exception升级到较高的层次。例如,不要让sqlexception延伸到业务层。业务层并不需要(不关心?)sqlexception。你有两种方法来解决这种问题:
l 转变sqlexception为另外一个checked exception,如果客户端并不需要恢复这种异常的话;
l 转变sqlexception为一个unchecked exception,如果客户端对这种异常无能为力的话;
多数情况下,客户端代码都是对sqlexception无能为力的,因此你要毫不犹豫的把它转变为一个unchecked exception,看看下边的代码:
public void dataaccesscode(){
try{
..some code that throws sqlexception
}catch(sqlexception ex){
ex.printstacktrace();
}
}
上边的catch块紧紧打印异常信息而没有任何的直接操作,这是情有可原的,因为对于sqlexception你还奢望客户端做些什么呢?(但是显然这种就象什么事情都没发生一样的做法是不可取的)那么有没有另外一种更加可行的方法呢?
public void dataaccesscode(){
try{
..some code that throws sqlexception
}catch(sqlexception ex){
throw new runtimeexception(ex);
}
}
上边的做法是把sqlexception转换为runtimeexception,一旦sqlexception被抛出,那么程序将抛出runtimeexception,此时程序被挂起并返回客户端异常信息。
如果你有足够的信心恢复它当sqlexception被抛出的时候,那么你也可以把它转换为一个有意义的checked exception, 但是我发现在大多时候抛出runtimeexception已经足够用了。
3. 不要创建没有意义的异常(try not to create new custom exceptions if they do not have useful information for client code.)
看看下面的代码有什么问题?
public class duplicateusernameexception
extends exception {}
它除了有一个“意义明确”的名字以外没有任何有用的信息了。不要忘记exception跟其他的java类一样,客户端可以调用其中的方法来得到更多的信息。
我们可以为其添加一些必要的方法,如下:
public class duplicateusernameexception
extends exception {
public duplicateusernameexception
(string username){....}
public string requestedusername(){...}
public string[] availablenames(){...}
}
在新的代码中有两个有用的方法:reqeuestedusername(),客户但可以通过它得到请求的名称;availablenames(),客户端可以通过它得到一组有用的usernames。这样客户端在得到其返回的信息来明确自己的操作失败的原因。但是如果你不想添加更多的信息,那么你可以抛出一个标准的exception:
throw new exception("username already taken");
更甚的情况,如果你认为客户端并不想用过多的操作而仅仅想看到异常信息,你可以抛出一个unchecked exception:
throw new runtimeexception("username already taken");
另外,你可以提供一个方法来验证该username是否被占用。
很有必要再重申一下,checked exception应该让客户端从中得到丰富的信息。要想让你的代码更加易读,请倾向于用unchecked excetpion来处理程序中的错误(prefer unchecked exceptions for all programmatic errors)。
4. document exceptions.
你可以通过javadoc’s @throws 标签来说明(document)你的api中要抛出checked exception或者unchecked exception。然而,我更倾向于使用来单元测试来说明(document)异常。不管你采用哪中方式,你要让客户端代码知道你的api中所要抛出的异常。这里有一个用单元测试来测试indexoutofboundsexception的例子:
public void testindexoutofboundsexception() {
arraylist blanklist = new arrayl