基本概念
发现错误的理想时机是在编译阶段,也就是在你试图运行程序之前。然而,编译期间并不能找出所有的错误,余下的问题必须在运行期间解决。
早期的错误处理模式(在C以及早期语言)通常会返回某个特殊值或者设置某个标志,并且假定接收者将对这个返回值或标志进行检查,以判定是否发生了错误。我们需要在程序中的许多地方去处理特定的错误。这使得处理代码变得复杂。
异常处理是Java中唯一正式的错误报告机制,并且通过编译器强制执行。异常有“我对此感到意外”的意思。异常情形是指阻止当前方法或作用域继续执行的问题。
问题出现了,也许你不知道应该如何处理,但你知道不应该置之不理,你要停下来看看是否能在其他什么地方能处理这个问题。Java中出现问题时异常处理机制接管程序,即异常处理程序开始发挥作用,它的任务是将程序从错误状态中恢复,以使程序要么换一种方式执行,要么继续运行下去。
抛出异常
抛出异常的一个例子:
/*对于对象引用t,传过来时可能没有初始化,所以我们要先进行检查
*如果出现问题,也就是t为空,我们可以创建一个代表错误信息的对象,并且用
*throw关键字将这个异常对象从当前环境中“抛出”
*/
if (t == null)
throw new NullPointerException;
总的来说,java中会发生如下几件事:
1、同java中创建新对象一样,将使用new在堆中创建异常对象
2、当前的执行路径被终止,也就是说发生异常的地方之后的代码不再执行。并且从当前环境中弹出对异常对象的引用。此时,异常处理机制开始接管程序。
捕获异常(try…catch…finally)
try块
异常如何被捕获,这就要涉及到try块。一般来说,在java中,如果一个方法在内部抛出了异常,那么这个方法将在抛出异常的过程中结束。如果我们不想该方法就此结束,那就需要try块。
try {
//Code that might generate exceptions
}
try,即为尝试,也就是说不管这个方法(或者这段代码)是否抛出异常(出现了问题),我们都要尝试一下,人总是那么执着。好了,既然我们一定要执行可能会产生问题的代码。但是出现了问题怎么办呢,不用担心,在java中出了问题由异常处理机制来解决,那异常处理机制怎么才能解决问题。这就要求我们首先要把可能会抛出异常的代码写在try块中。
总的来说,因为在这个块里”尝试”各种方法调用,所以称之为try块。还有进一步的好处就是,我们把所有的动作(语句)都放在try块,然后只需要同一个地方就可以捕获所有异常,这也使得代码更容易编写和阅读。
catch子句
抛出的异常必须在某个地方得到处理。这个“地方”就是异常处理程序。异常处理程序紧跟在try块之后,以关键字catch表示:
try {
//Code that might generate exceptions
} catch(Type1 id1) {
//Handle exceptions of Type1
} catch(Type2 id2) {
//Handle exceptions of Type2
} catch(Type3 id3) {
//Handle exceptions of Type3
}
//etc...
每个catch子句(异常处理程序)看起来就像是接收一个且仅接收一个特殊类型的参数的方法。注意,只有匹配的catch子句才能得到执行。这与switch语句不一样,switch语句需要在每一个case后面跟一个break,以避免执行后续的case子句。
finally子句
对于一些代码,可能会希望无论try块中的异常是否抛出,它们都得到执行。因为前面我们已经提到,异常发生的地方之后的代码不会再执行,如果我们非常非常希望执行某段代码,但是这段代码在逻辑上一定要写在异常发生处的后面,这就矛盾了。这时,finally子句就能帮到我们。
try {
//Code that might generate exceptions
} catch(A a1) {
//Handle exceptions of A
} catch(B b2) {
//Handle exceptions of B
} catch(C c3) {
//Handle exceptions of C
} finally {
//Activities that happen every time
}