Java异常略解
异常就是程序运行时可能产生的某种特殊情况,通过异常处理可以改变程序的控制流程,让程序能对错误做出处理。
1.获得异常信息
异常对象调用如下方法得到或输出有关异常的信息:
- public String getMessage();
- public void printStackTrace();
- public String toString();
2.try-catch-finally
将可能出现异常的操作放在try部分,当try部分抛出异常对象或调用一个可能抛出异常对象的方法且该方法抛出了异常对象,那么执行相应的catch部分。无论是否发生异常,总是执行finally部分。
一般形式如下:
try{
try块中放可能发生异常的代码;
如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话);
如果发生异常,则尝试去匹配catch块;
}
catch(ExceptionSubClass1 e){
每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中;
catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常;
在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问;
如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部调用者中去匹配异常处理器;
如果当前try块发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行,然后去外围调用者中寻找合适的catch块;
如果try中没有发生异常,则所有的catch块将被忽略;
}
catch(ExceptionSubClass2 e){
如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义;
}
finally{
finally块通常是可选的。
无论异常是否发生,异常是否匹配被处理,finally都会执行。
一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
}
3.异常类
Unchecked Exception 与 Checked Exception 是对于javac而言的。
- Unchecked Exception
javac在编译时,不会提示和发现这样的异常。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码错误。
- Checked Exception
javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。
- 处理机制
异常是在执行某个方法时引发的,且当一个方法发生了异常,该方法的调用者也会被异常影响。当这些被影响的方法以异常信息输出时,就形成的了异常追踪栈。异常最先发生的地方,叫做异常抛出点。
异常从被调用方法(栈顶)向调用方法(栈底)回溯,叫做异常的冒泡。异常的冒泡是为了在当前发生异常的方法或者这个方法的调用者中找到最近的异常处理程序。若没有特别的处理,则交由JRE处理,程序终止。若有处理,异常被处理完后,执行流会接着在“处理了这个异常的catch代码块”后面接着执行。这种策略叫做终结式异常处理模式。
- throws函数声明
如果一个方法内部的代码会抛出Checked Exception,而方法自己又没有完全处理掉,则javac保证必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。
throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。
当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内,即用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 ,这是为了支持多态。
采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。
一般形式如下:
public void F() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN
{
F内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。
}
- throw 异常抛出
可以通过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。
throw 语句必须写在方法中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。
一般形式如下:
public void F()
{
throw new Exception(或其子类)("Message");
}
- 自定义异常
如果要自定义检查异常类,则扩展Exception类。如果要自定义非检查异常类,则扩展RuntimeException类。
要求至少满足下列其中一条:
- 包含一个无参构造函数
- 包含 一个带有String参数的构造函数,并传递给父类的构造函数。
- 包含一个带有String参数和Throwable参数,并都传递给父类构造函数
- 包含一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
4.finally语句与return
- finally语句不被执行的条件
- try语句没有被执行。
- try块中出现System.exit(0);之类语句。
- finally块语句与try-catch块中return语句的执行顺序
- finally块语句在try-catch块中return语句执行(计算出返回值【注:若返回值为变量,则此变量切实被修改】)之后,返回结果之前执行(若多次return,只在最终的返回结果之前执行)。
- finally块中修改语句可能会影响try-catch块中已经确定的返回值。
- finally块中若有return语句,将会覆盖try-catch块中的return语句返回。且导致finally块之后的语句不可达。
- finally块中若有异常,将会覆盖try-catch块中的异常。
- 发生异常后catch块中return语句执行情况与未发生异常的try块中return语句执行情况完全一致。