Java中如何正确处理异常
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1 引言
在JAVA语言出现以前,传统的异常处理方式多采用返回值来标识程序出现的异常情况,这种方式虽然为程序员所熟悉,但却有多个坏处。
首先,一个API可以返回任意的返回值,而这些返回值本身并不能解释该返回值是否代表一个异常情况发生了和该异常的具体情况,需要调用API的程序自己判断并解释返回值的含义。
其次,并没有一种机制来保证异常情况一定会得到处理,调用程序可以简单的忽略该返回值,需要调用API的程序员记住去检测返回值并处理异常情况。这种方式还让程序代码变得晦涩冗长,当进行IO操作等容易出现异常情况的处理时,你会发现代码的很大部分用于处理异常情况的switch分支,程序代码的可读性变得很差。
上面提到的问题,JAVA的异常处理机制提供了很好的解决方案。通过抛出JDK 预定义或者自定义的异常,能够表明程序中出现了什么样的异常情况;而且JAVA 的语言机制保证了异常一定会得到恰当的处理;合理的使用异常处理机制,会让程序代码清晰易懂。
2 JAVA异常的处理机制
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java 虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finally块代码被执行和当前线程的所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被中止。
3 JAVA异常的类层次
JAVA异常的类层次如下图所示:
图1 JAVA异常的类层次
Throwable是所有异常的基类,程序中一般不会直接抛出Throwable对象,Exception和Error是Throwable的子类,Exception下面又有RuntimeException 和一般的Exception两类。可以把JAVA异常分为三类:
第一类是Error,Error表示程序在运行期间出现了十分严重、不可恢复的错误,在这种情况下应用程序只能中止运行,例如JAVA 虚拟机出现错误。Error 是一种unchecked Exception,编译器不会检查Error是否被处理,在程序中不用捕获Error类型的异常;一般情况下,在程序中也不应该抛出Error类型的异常。
第二类是RuntimeException, RuntimeException 是一种unchecked Exception,即表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
第三类是一般的checked Exception,这也是在编程中使用最多的Exception,所有继承自Exception并且不是RuntimeException的异常都是checked Exception,如图1中的IOException和ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中
声明抛出checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。checked Exception用于以下的语义环境:
(1)该异常发生后是可以被恢复的,如一个Internet连接发生异常被中止后,可以重新连接再进行后续操作。
(2)程序依赖于不可靠的外部条件,该依赖条件可能出错,如系统IO。
(3)该异常发生后并不会导致程序处理错误,进行一些处理后可以继续后续操作。
4 JAVA异常处理中的注意事项
合理使用JAVA异常机制可以使程序健壮而清晰,但不幸的是,JAVA异常处理机制常常被错误的使用,下面就是一些关于Exception的注意事项:
1.不要忽略checked Exception
请看下面的代码:
try
{
method1(); //method1抛出ExceptionA
}
catch(ExceptionA e)
{
e.printStackTrace();
}
上面的代码似乎没有什么问题,捕获异常后将异常打印,然后继续执行。事实上在catch块中对发生的异常情况并没有作任何处理(打印异常不能是算是处理异常,因为在程序交付运行后调试信息就没有什么用处了)。这样程序虽然能够继续执行,但是由于这里的操作已经发生异常,将会导致以后的操作并不能按照预期的情况发展下去,可能导致两个结果:
一是由于这里的异常导致在程序中别的地方抛出一个异常,这种情况会使程序员在调试时感到迷惑,因为新的异常抛出的地方并不是程序真正发生问题的地方,也不是发生问题的真正原因;
另外一个是程序继续运行,并得出一个错误的输出结果,这种问题更加难以捕捉,因为很可能把它当成一个正确的输出。
那么应该如何处理呢,这里有四个选择:
(1)处理异常,进行修复以让程序继续执行。
(2)重新抛出异常,在对异常进行分析后发现这里不能处理它,那么重新抛出异常,让调用者处理。
(3)将异常转换为用户可以理解的自定义异常再抛出,这时应该注意不要丢失原始异常信息(见5)。
(4)不要捕获异常。
因此,当捕获一个unchecked Exception的时候,必须对异常进行处理;如果认为不必要在这里作处理,就不要捕获该异常,在方法体中声明方法抛出异常,由上层调用者来处理该异常。