副标题#e#
1、当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。
例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。
2、Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常 会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。
也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。
1、将异常直接显示在页面或客户端
将异常直接打印在客户端的例子屡见不鲜,一旦程序运行出现异常,默认情况下容器将异常堆栈信息直接打印在页面上。从客户角度来说,任何异常都没有实际意义,绝大多数的客户也根本看不懂异常信息,软件开发也要尽量避免将异常直接呈现给用户,一定要在前端展示层对异常进行封装后展示。目前绝大多数应用都是前后端分离的模式,这种直接打印异常的情况已经相对改善了很多,不过我们在编码时还是要特别注意下这个原则。
2、忽略异常
如下异常处理只是将异常输出到控制台,没有任何意义。而且这里出现了异常并没有中断程序,进而调用代码继续执行,导致更多的异常。
public void retrieveObjectById(Long id) {
try {
//..some code that throws SQLException
} catch (SQLException ex) {
/**
*了解的人都知道,这里的异常打印毫无意义,仅仅是将错误堆栈输出到控制台。
* 而在 Production 环境中,需要将错误堆栈输出到日志。
* 而且这里 catch 处理之后程序继续执行,会导致进一步的问题*/
ex.printStacktrace();
}
}
捕获了异常缺不进行处理,这是我们在写代码时候的大忌,可以重构成:
public void retrieveObjectById(Long id) {
try {
//..some code that throws SQLException
} catch (SQLException ex) {
throw new RuntimeException("Exception in retieveObjectById”, ex);
} finally {
//clean up resultset, statement, connection etc
}
}
3、将异常包含在循环语句块中
如下代码所示,异常包含在 for 循环语句块中。
for (int i = 0; i < 100; i++) {
try {
} catch (XXXException e) {
//….
}
}
我们都知道异常处理占用系统资源。一看,大家都认为不会犯这样的错误。换个角度,类 A 中执行了一段循环,循环中调用了 B 类的方法,B 类中被调用的方法却又包含 try-catch 这样的语句块。褪去类的层次结构,代码和上面如出一辙。
4、利用 Exception 捕捉所有潜在的异常
一段方法执行过程中抛出了几个不同类型的异常,为了代码简洁,利用基类 Exception 捕捉所有潜在的异常,如下例所示:
public void retrieveObjectById(Long id) {
try {
//…抛出 IOException 的代码调用
//…抛出 SQLException 的代码调用
} catch (Exception e) {
//这里利用基类 Exception 捕捉的所有潜在的异常,如果多个层次这样捕捉,会丢失原始异常的有效信息
#p#副标题#e#
throw new RuntimeException("Exception in retieveObjectById”, e);
}
}
估计大部分程序员都会有这种写法,为了省事简便,直接一个顶层的exception来捕获所有可能出现的异常,这样虽然可以保证异常肯定会被捕捉到,但是程序却无法针对不同的错误异常进行对应正确的处理,可以重构成:
public void retrieveObjectById(Long id) {
try {
//..some code that throws RuntimeException, IOException, SQLException
} catch (IOException e) {
//仅仅捕捉 IOException
#p#副标题#e##p#分页标题#e#
throw new RuntimeException(/*指定这里 IOException 对应的错误代码*/code, "Exception in retieveObjectById”, e);
} catch (SQLException e) {
//仅仅捕捉 SQLException
throw new RuntimeException(/*指定这里 SQLException 对应的错误代码*/code, "Exception in retieveObjectById”, e);
}
}
5、异常包含的信息不能充分定位问题
异常不仅要能够让开发人员知道哪里出了问题,更多时候开发人员还需要知道是什么原因导致的问题,我们知道 java .lang.Exception 有字符串类型参数的构造方法,这个字符串可以自定义成通俗易懂的提示信息。
简单的自定义信息开发人员只能知道哪里出现了异常,但是很多的情况下,开发人员更需要知道是什么参数导致了这样的异常。这个时候我们就需要将方法调用的参数信息追加到自定义信息中。下例只列举了一个参数的情况,多个参数的情况下,可以单独写一个工具类组织这样的字符串。