3.设计一个新的异常类
在设计一个新的异常类时,首先看看是否真正的需要这个异常类。一般情况下尽量不要去设计新的异常类,而是尽量使用java中已经存在的异常类。
如java 代码IllegalArgumentException, UnsupportedOperationException
不管是新的异常是chekced异常还是unChecked异常。我们都必须考虑异常的嵌套问题。
java 代码
public void methodA()throws ExceptionA{
…..
throw new ExceptionA();
}
方法methodA声明会抛出ExceptionA.
public void methodB() throws ExceptionB
methodB声明会抛出ExceptionB,当在methodB方法中调用methodA时,如何将ExceptionA继续往上抛出。
一个办法是把methodB声明会抛出ExceptionA, 但这样已经改变了MethodB的方法签名。一旦改变,则所有调用methodB的方法都要进行改变。
另一个办法是把ExceptionA封装成ExceptionB,然后再抛出。如果我们不把ExceptionA封装在ExceptionB中,就丢失了根异常信息,使得无法跟踪异常的原始出处。
java 代码
public void methodB()throws ExceptionB{
try{
methodA();
……
}catch(ExceptionA ex){
throw new ExceptionB(ex);
}
}
如上面的代码中,ExceptionB嵌套一个ExceptionA.我们暂且把ExceptionA称为“起因异常”,因为ExceptionA导致了ExceptionB的产生。这样才不使异常信息丢失。
所以我们在定义一个新的异常类时,必须提供这样一个可以包含嵌套异常的构造函数。并有一个私有成员来保存这个“起因异常”。
java 代码
public Class ExceptionB extends Exception{
private Throwable cause;
public ExceptionB(String msg, Throwable ex){
super(msg);
this.cause = ex;
}
public ExceptionB(String msg){
super(msg);
}
public ExceptionB(Throwable ex){
this.cause = ex;
}
}
当然,我们在调用printStackTrace方法时,需要把所有的“起因异常”的信息也同时打印出来。所以我们需要覆写printStackTrace方法来显示全部的异常栈跟踪。包括嵌套异常的栈跟踪。
java 代码
public void printStackTrace(PrintStrean ps){
if(cause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
cause.printStackTrace(ps);
}
}
一个完整的支持嵌套的checked异常类源码如下。我们在这里暂且把它叫做NestedException
java 代码
public NestedException extends Exception{
private Throwable cause;
public NestedException (String msg){
super(msg);
}
public NestedException(String msg, Throwable ex){
super(msg);
This.cause = ex;
}
public Throwable getCause(){
return (this.cause == null ? this :this.cause);
}
public getMessage(){
String message = super.getMessage();
Throwable cause = getCause();
if(cause != null){
message = message + “;nested Exception is ” + cause;
}
return message;
}
public void printStackTrace(PrintStream ps){
if(getCause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
getCause().printStackTrace(ps);
}
}
public void printStackTrace(PrintWrite pw){
if(getCause() == null){
super.printStackTrace(pw);
}
else{
pw.println(this);
getCause().printStackTrace(pw);
}
}
public void printStackTrace(){
printStackTrace(System.error);
}
}
同样要设计一个unChecked异常类也与上面一样。只是需要继承RuntimeException。
4. 如何记录异常
作为一个大型的应用系统都需要用日志文件来记录系统的运行,以便于跟踪和记录系统的运行情况。系统发生的异常理所当然的需要记录在日志系统中。
java 代码
public String getPassword(String userId)throws NoSuchUserException{
UserInfo user = userDao.queryUserById(userId);
If(user == null){
Logger.info(“找不到该用户信息,userId=”+userId);
throw new NoSuchUserException(“找不到该用户信息,userId=”+userId);
}
else{
return user.getPassword();
}
}
public void sendUserPassword(String userId) throws Exception {
UserInfo user = null;
try{
user = getPassword(userId);
//……..
sendMail();
//
}catch(NoSuchUserException ex)(
logger.error(“找不到该用户信息:”+userId+ex);
throw new Exception(ex);
}
我们注意到,一个错误被记录了两次.在错误的起源位置我们仅是以info级别进行记录。而在sendUserPassword方法中,我们还把整个异常信息都记录了。
笔者曾看到很多项目是这样记录异常的,不管三七二一,只有遇到异常就把整个异常全部记录下。如果一个异常被不断的封装抛出多次,那么就被记录了多次。那么异常倒底该在什么地方被记录?
异常应该在最初产生的位置记录!
如果必须捕获一个无法正确处理的异常,仅仅是把它封装成另外一种异常往上抛出。不必再次把已经被记录过的异常再次记录。
如果捕获到一个异常,但是这个异常是可以处理的。则无需要记录异常
java 代码
public Date getDate(String str){
Date applyDate = null;
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
try{
applyDate = format.parse(applyDateStr);
}
catch(ParseException ex){
//乎略,当格式错误时,返回null
}
return applyDate;
}
捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息
java 代码
try{
……
String sql=”select * from userinfo”;
Statement s = con.createStatement();
……
Catch(SQLException sqlEx){
Logger.error(“sql执行错误”+sql+sqlEx);
}
究竟在哪里记录异常信息,及怎么记录异常信息,可能是见仁见智的问题了。甚至有些系统让异常类一记录异常。当产生一个新异常对象时,异常信息就被自动记录。
java 代码
public class BusinessException extends Exception {
private void logTrace() {
StringBuffer buffer=new StringBuffer();
buffer.append("Business Error in Class: ");
buffer.append(getClassName());
buffer.append(",method: ");
buffer.append(getMethodName());
buffer.append(",messsage: ");
buffer.append(this.getMessage());
logger.error(buffer.toString());
}
public BusinessException(String s) {
super(s);
Trace();
}
这似乎看起来是十分美妙的,其实必然导致了异常被重复记录。同时违反了“类的职责分配原则”,是一种不好的设计。记录异常不属于异常类的行为,记录异常应 该由专门的日志系统去做。并且异常的记录信息是不断变化的。我们在记录异常时应该给出更丰富些的信息。以利于我们能够根据异常信息找到问题的根源,以解决 问题。
虽然我们对记录异常讨论了很多,过多的强调这些反而使开发人员更为疑惑,一种好的方式是为系统提供一个异常处理框架。由框架来决定是否记录异常和怎么记录异常。而不是由普通程序员去决定。但是了解些还是有益的。