异常
#异常简介
程序运行时, 会发生不被期望的错误,它的发生阻止了程序的正常运行,这就是异常。
异常发生时,是任由程序自生自灭,中途退出,输出错误给用户?还是采取其他行为处理错误?或是像C语言,用函数返回值作为执行状态?
如果因为异常而造成的数据丢失,那会带来很严重的后果,所以处理异常应至少做到以下几点:
- 向用户通告错误
- 保存所有的工作结果
- 允许用户以妥善的形式退出程序
Java提供了优良的解决方法:异常处理机制。
异常的分类和结构
有的错误为用户引起的,有的是程序内部代码错误引起的,还有其他的错误是因为一些外界物理条件改变引起的。
Java将异常分为两类
unckecked exception(非检查异常)
包括 Error 和 RuntimeException。在Javac编译器编译时不会提示和发现这样的异常,也不要求在程序中处理这些异常。
checked exception(检查异常)
包括 IOException。Javac编译器强制程序员对这些异常进行相应的处理(调用try,catch,finally,throws等)否则,编译器就会报错。
结构:
1. Error 错误
该异常描述了Java运行时系统内部的错误和资源耗尽的错误。在编写程序时不需要用异常机制处理这种错误。对待这种错误,应通告用户并且尽力时程序安全的退出。
2. RuntimeException 运行时异常
由于程序编写的错误引起的异常。应在程序内加入相应的判断从而避免此类异常的发生。例如通过检查数组下标从而防止下标越界的异常,使用变量前先检查是否为null来避免空指针异常。
3. IOException I/O异常
例如要打开一个不存在文件时,异常就发生了,这些异常在编译时不能被简单地忽略。
#处理异常
try…catch…finally语句块来处理异常
用try…catch…finally语句块来处理异常,在代码抛出异常时,可以将异常捕获(catch),利用捕获的对象获取异常的信息,可以做出相关的操作,如将异常信息打印,包括异常的类型和堆栈的内容等,之后finaly处理后序的操作.
使用方法:
try{
} catch (Exception aException){
}finally{
}
try
- 块里执行会发生异常的代码
- 如果没有发生异常,继续执行finally块里的
- 如果发生异常,匹配catch块的语句
catch
- 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。
- 在catch块中可以使用这个块的异常参数来获取异常的相关信息。
- 如果try中没有发生异常,则所有的catch块将被忽略。
finally
- 无论异常是否发生,异常是否匹配被处理,finally都会执行。
- finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
注 :
- try catch finally 不同的块中的局部变量作用域不同,不可共享. 如想要共享,则需要将声明放在所有块的外面.
- 每一个catch块处理一个异常,一个try可以接多个catch,异常自动从上而下匹配.
- java中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说,当try代码块里的语句发生异常,则try后的语句不在执行,而是跳转到catch匹配的地方处理异常. 然后从finally接着执行. 这种策略叫做:termination model of exception handling(终结式异常处理模式).
Throws 声明
一个方法不仅要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误.
所以方法应该在其首部声明所有可能抛出的异常
用throws抛出异常.如果一个方法内部的代码会抛出异常,并且没有被捕获(try catch处理),则必须要将其声明继续抛出,将异常抛给方法的调用者来处理捕获。
样例
public void func() throws IOException {
}
样例声明了IOException异常,则方法里只要发生了IOException异常或者改异常的子类,该异常就会被抛出到上层处理.
选择处理(try catch)还是传递(throws)?
通常,应该捕获哪些知道如何处理的异常,而抛出哪些不知道如何处理的异常交给上层处理.
使用异常的技巧
1. 不要用异常代替简单的测试
编写一段代码,试着大量地对一个空栈进行出栈操作.
普通的检查代码 :
if(!aStack.empty) 如果不为空
aStack.pop(); 出站
使用异常处理的代码 :
try {
aStack.pop();
}catch (EmptyStackException e)
{
......
}
测试下来二者的耗时相差数十倍.捕获异常耗时大大超过了前者
所以, 应只在必要的时候使用异常.
2. 不要过分的细化
最好不要将每一条语句都分装在独立的try语句中.
Stack aStack;
PrintStream out;
for(int i = 0; i < 100; i++)
{
try{
int temp = aStack.pop()
}catch (EmptyStackException) {
.....
}
try{
out.writeInt(temp);
}catch (IOException) {
......
}
}
这样会导致代码量的急剧膨胀.
最好这要写
Stack aStack;
PrintStream out;
try{
for(int i = 0; i < 100; i++)
{
int temp = aStack.pop()
out.writeInt(temp);
}
}catch (EmptyStackException) {
.....
}catch (IOException) {
......
}
}
这样代码看起来清晰,并且当其中一个操作出现问题,就会停止执行错误的循环.
3. 利用异常层次结构
不要只抛出RuntimeException,应该寻找更加适当的子类或者创建适合的异常类.
不要只捕获Thowable异常,否则,会让代码变得更加难度和难以维护.