异常处理
根据自己几年的项目经验,说说自己对异常处理的认识。
首先看一段代码
我们经常会看到上述这样的代码。你是不是还在为你捕捉了异常,增强了程序健壮性而沾沾自喜啊!但是这些代码除了在主程序中,永远不要捕捉它。除非你有特别的理由。
使用异常处理,我们有一个原则:永远不处理你不知道怎么处理的异常,换句换说不遮掩异常。通过这个原则,暴露出了初学者经常犯得一个错误。普便使用捕捉一般异常。
异常本质上是对程序接口隐含假设的一种违反。一般一类错误都会有一个异常类来进行处理。如流操作中的IOException,线程操作中的InterruptedException……对错误进行分类整理后,不同类型的错误走不同的处理程序。这样,程序思路更清晰明了。一般80%的错误,都已经被封装好了。但有时候,还是不很够用。有时,我们需要自定义一些错误处理。(如登录界面中,我们需要检查用户名,密码是否匹配,如不匹配则给出提示,禁止其登录。)这也能归为一类错误。那自定义错误处理如何实现呢?
简单举例说明一下
import java.lang.Exception; //测试主程序 //第一种定义方式,继承Exception类 //第二种定义方式:继承Throwable类 class MySecondException extends Throwable { |
其实,上面的那个例子还隐藏着另外的一个问题。异常应该在哪里进行处理呢?这个一般是对可预知的异常在原地进行处理,不能处理的往上层抛出。当然,这也看异常的性质,如果,你是传递异常(如上面登录的举例,信息不匹配的信息就是一个异常的传递,此时,我们就应该在业务逻辑层捕捉到异常,往上抛给界面层进行处理)。而这些关于程序逻辑的自定义异常在设定时,一定要按照某种原则分好类。
这里要说明一下,异常的抛出与截获需要很多的cpu时间,所以,使用时,应该谨慎。
最后,说几点准则用于确保库正确处理异常。(摘录自异常处理msdn)
1、 不要通过在框架代码中捕捉非特定异常(如System.Exception、System.SystemException)来处理错误。(如果捕捉异常是为了再次引发或传输给其他线程,则可以捕捉这些异常。)
2、避免通过在应用程序代码中捕捉非特定异常(如 System.Exception、System.SystemException 等)来处理错误。 某些情况下,可以在应用程序中处理错误,但这种情况极少。 (应用程序不应该处理异常,否则可能导致意外状态或可利用状态。 如果不能预知所有可能的异常原因,也不能确保恶意代码不能利用产生的应用程序状态,则应该允许应用程序终止,而不是处理异常。)
3、如果捕捉异常是为了传输异常,则不要排除任何特殊异常。(只捕捉能够合法处理的异常,而不要在 catch 子句中创建特殊异常的列表。 在非特定异常处理程序中,不能处理的异常不应视为特殊处理的特殊情况。)
4、如果了解特定异常在给定上下文中引发的条件,请考虑捕捉这些异常。(应该只捕捉可以从中恢复的异常。 例如,尝试打开不存在的文件而导致的 FileNotFoundException可以由应用程序处理,因为应用程序可以将问题传达给用户,并允许用户指定其他文件名或创建该文件。 如果打开文件的请求会生成 ExecutionEngineException,则不应该处理该请求,因为没有任何把握可以了解该异常的基础原因,应用程序也无法确保继续执行是安全的。 )
5、不要过多使用 catch。 通常应允许异常在调用堆栈中往上传播。 (捕捉无法合法处理的异常会隐藏关键的调试信息。 )
6、使用 try-finally 并避免将 try-catch 用于清理代码。 在书写规范的异常代码中,try-finally 远比 try-catch 更为常用。 (使用 catch 子句是为了允许处理异常(例如,通过纪录非致命错误)。 无论是否引发了异常,使用 finally 子句即可执行清理代码。 如果分配了昂贵或有限的资源(如数据库连接或流),则应将释放这些资源的代码放置在 finally 块中。)
7、捕捉并再次引发异常时,首选使用空引发。 这是保留异常调用堆栈的最佳方式。
8、不要使用无参数 catch 块来处理不符合 CLS 的异常(不是从 System.Exception 派生的异常)。 支持不是从 Exception 派生的异常的语言可以处理这些不符合 CLS 的异常。