Java的异常相比于C语言这种没有错误机制的语言是一个很大的进步,但Java中的异常并没有想象中的强大。异常的初衷是能在程序运行时报告错误并从错误中恢复,但是这种恢复很少(<10%)能实现,报告功能是异常处理的精髓。
Java坚定的强调所有的错误都以异常的形式报告这一事实
抛出:将错误信息传播到"更大"的环境中
当抛出异常后,有几件事会随之发生。首先,同java中其他对象的创建一样,使用new 在堆上创建异常对象。然后,当前的执行路径被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态恢复,以使程序要么换一种方式运行,要么继续运行下去
throw new Exception();
//new Exception()将生成一个异常对象的引用,throw接收这个引用并从当前作用域退出,throw类似于return
通常异常对象中仅有的信息就是异常的类型
catch(Exception e){ catch(NullPointerException e){
... 会在编译 ...
} 期报错 }
catch(NullPointerException e){ ------> catch(Exception e){
... ...
} }
(正确的做法,先捕获小的异常)
恢复模型:
使用异常处理可以是程序从错误中恢复
while(true){
try{
//...
break;
}catch(){
//...
}
}
同时还可以定义变量在尝试n次之后从错误中退出
受检异常
编译时被强制检查的异常,如 void f() throws XXException
非受检异常
均继承自RuntionException,它们会自动被java虚拟机抛出。这种异常属于错误,将被自动捕获,但可在代码中抛出RuntimeException。当程序产生非受检异常时程序会在退出前自动调用printStackTrace()方法。非受检异常可以被捕获
JDK异常结构图
Throwable
|--Error
|--...OutOfMemoryError等
|--Exception
|--ClassNotFoundException等
|--RuntimeException
|--...
printStackTrace() 调用栈显示了把你带到异常抛出地点的方法调用序列
e.fillInStackTrace() 将返回一个throwable对象,用于在catch内重新抛出异常后更新异常点信息。如果不这么做,printStackTrace将显示原来的抛出点调用栈轨道
在捕获异常后抛出另一种(注意是另一种)异常,将丢失原来的异常信息,使用异常链可以保存原始异常信息,e2.initCase(e)或构造器(e).
异常的基本概念是用名称代表发生的问题,并且异常的名称可以望问知意
finally
在异常没有被当前异常处理程序捕获的情况下,异常机制也会在跳至更高的异常处理之前执行finally (break、continue、return也是)
在有finally的try中执行会抛出RuntimeException的语句时,即便程序产生了异常并退出,也会在退出前执行finally子句
bug:当try中的异常尚未得到处理而在finally中再次抛出异常时,finally中的异常就会替代原有的异常,导致原有异常信息丢失,或者在finally中加上return也会出现这种情况
异常与方法的覆盖和重载
override时子类的方法可以比父类抛出更少的异常(甚至不抛出),但不能多于。
异常说明本身并不属于方法类型的一部分,因此不能基于异常说明重载方法
除了内存的清理外,所有的清理(如文件)都不会自动产生。所以必须告诉客户端程序员,这是他们的责任。
构造器异常
对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的方式是使用嵌套try语句。
try{
in = new FileInput("1.txt"); //FileInput会在构造器中抛出异常
//文件操作
}finally{
in.close();
}
}
这种做法有可能在构造阶段发生异常,此时in并没有指向文件,却调用了close方法。
更好的做法:
try{
in = ..;
try{
//文件操作
}finally{
in.close();
}
}catch(){
//文件构造阶段出错,不需要关闭文件
}
派生类构造器不能捕获其基类构造器所抛出的异常
异常处理的原则:只有在知道如何处理的情况在才捕获异常。
异常处理的目标:把错误处理代码同错误发生的地点相分离。
吞食则有害(harmful if swallowed)
强静态语言,在编译时就做类型检查的语言,java
反射和泛型就是用来补偿静态类型检查所带来的过多限制
好的程序设计语言能帮助程序员写出好程序,但无论哪种语言都避免不了程序员用他写出坏程序
处理异常的几种方式
1.把异常传给控制台 main(String args[])throws XXException
最简单而又不用写多少代码就能保护异常信息的方法。
2.将"受检异常"转换为"非受检异常"
当你不知道该如何去处理一个方法的异常,并且不希望吞掉异常或者打印无用信息时,可以使用异常链来抛出RuntimeException(throw new RuntimeException(e)),此时异常链会替你保存原始异常信息
编译器不会强制你捕获RuntimeException,但你可以捕获
异常使用指南:见P281
在知道如何处理的情况下才捕获异常
...
...