1- try
将可预见可能抛出异常的代码包含在try语句块中,也就是将一些会抛出异常的方法调用放在这个语句块,以便后续处理
2- 异常
- Throwable
错误和异常的超类(Error和Exception都继承自Throwable),包含了其线程创建时线程执行堆栈的快照,通过printStackTrace()方法打印堆栈跟踪数据信息。
- Exception
被检查异常的顶级类,** 除了RuntimeException ,Exception类型的异常编译器会检查这种异常,需要对应的异常处理 **,否则会因为异常抛出导致程序异常终止,我们在开发的时候主要就是捕获这种异常。
- RuntimeException
** 运行时被检查异常,继承自Exception,但是这种异常发生在虚拟机运行期间,编译器不会检查这种异常 ,如果我们调用的方法可能抛出RuntimeException 类型的异常,但是并没有相应的try/catch进行处理,最后能通过编译,是因为编译器并不会在编译期间对这种异常进行检查。 如果代码会产生RuntimeException,则需要调整代码进行避免 **,如 ** ArithmeticException **
- Error
继承自Throwable,** 编译器不会对错误进行检查 **。用于指示严重错误的发生,说明已经发生了无法挽回的错误,程序无法修复这种错误,直接导致程序因为错误而终止。如 ** VirtualMachineError **
** 下面重点介绍Exception类型的常用异常 **
以下都是Exception的子类,表示特定的用途
- IllegalArgumentException 参数的值不合适
- IllegalStateException 参数的状态不合适
- NullPointerException 在null被禁止的情况下参数值为null
- IndexOutOfBoundsException 下标越界
- ConcurrentModificationException 在禁止并发修改的情况下,对象检测到并发修改
- UnsupportedOperationException 对象不支持客户请求的方法
3- catch
捕获try语句块中的Exception异常,并进行相应的处理
1)编译的时候有些异常的捕获,如Exception,IllegalStateException,NullPointerException等(java.lang包的异常)并不要求try语句中含有抛出异常的方法调用;
2)但是有些异常的捕获如ObjectStreamException,IOException等(java.io包的异常)并要求try语句中必须含有抛出异常的方法调用,否则编译错误
- 一个try语句块可以有多个catch子句来捕获不同的异常,但是** 同时只能捕获一个异常, 在异常发生的代码段,后续的代码得不到执行 **,转而执行对应的catch子句,并正常退出方法的调用。
例子如下:
- 通过捕获异常的超类来捕获
例子如下:
- catch中如果没有捕获到抛出的异常,则继续向上级调用者抛出异常
例子如下:
4- throw 和 throws
** throw **
用于在方法体内抛出异常,如果方法内部抛出异常,则此方法声明上可以通过throws声明抛出的异常,也可以什么都不声明,对抛出的异常并没有什么影响。
比如
** throws **
用于方法声明上,声明可能抛出的异常,见例子4
需要注意的是异常声明在继承时多态隐含的关系(** 对父类方法进行方法重写 ),虽然 异常声明不是方法签名的一部分 **
- ** 如果父类的方法没有声明抛出异常,子类覆盖的方法也不能声明抛出任何异常。**
- ** 如果父类的方法声明抛出特定异常E,子类覆盖的方法要么不声明抛出任何异常,要么声明抛出E异常或者E异常的子类(隐含的继承关系)。**
5- finally
表示的不管try方法块是正常执行完毕,还是抛出异常退出try语句块,finally都会在程序离开try语句块后执行,用来做最后的资源回收等操作。
- ** try中的return,catch中的return,finally中的return **
从中可以看出finally的return最终会覆盖try中的return语句,catch中没有捕获到异常,没有得到执行,** finally > catch > try 按照等级对return进行覆盖 **。
- ** try中的System.exit() **
** 当try执行System.exit(0/1)都将立马退出程序,finally中的内容是得不到执行的。 **
- 守护进程中的finally
- ** try/finally组合 **
setObj方法抛出异常,tryFinally方法调用setObj,并没有捕获异常,而是由tryFinally上级调用main方法来捕获,但是如果main方法没有捕获,则会因为异常抛出而没有处理而异常终止程序,参见AQS的acquireQueued方法
总结
避免将异常处理用于流程控制,也不能频繁的检查捕获不必要的异常,因为频繁异常的捕获和抛出(获取抛出异常线程的堆栈信息等操作)将消耗大量的系统性能,而且抛出的异常应尽量使用标准的异常类,来做到明确表达异常的含义。