一、Java异常介绍
Java异常是Java提供的一种识别及响应错误的一致性机制。Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。
1.Java异常框架
Java提供了丰富的异常类,这些异常类之间有严格的继承关系。Java常见的异常类继承关系如下图所示:
Throwable为顶层父类,Throwable又派生出Error类和Exception类。
Exception及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
Error也是Throwable的子类,它用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。
2.检查异常与非检查异常
检查异常(checked exception):除了Error 和 RuntimeException的其它异常。也就是说Exception类本身,以及Exception的子类中除了RuntimeException之外的其它子类都属于被检查异常。它的特点是Java编译器会检查它。此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。例如,CloneNotSupportedException就属于被检查异常。当通过clone()接口去克隆一个对象,而该对象对应的类没有实现Cloneable接口,就会抛出CloneNotSupportedException异常。
非检查异常(unckecked exception):Error和RuntimeException以及他们的子类,其特点是Java编译器不去检查它。当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生Error错误。RuntimeException是那些可能在Java虚拟机正常运行期间抛出的异常的超类,如除数为零时,抛出ArithmeticException异常,RuntimeException是ArithmeticException的超类。
3.运行时异常
运行时异常(Runtime Exception):即RuntimeException,其特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try catch捕获,也没有用throws抛出,还是会编译通过,如除数为零的ArithmeticException、错误的类型转换、数组越界访问和试图访问空指针等。
常见的RuntimeException
- NullPointerException - 空指针引用异常
- ClassCastException - 类型强制转换异常。
- IllegalArgumentException - 传递非法参数异常。
- ArithmeticException - 算术运算异常
- ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
- IndexOutOfBoundsException - 下标越界异常
- NegativeArraySizeException - 创建一个大小为负数的数组错误异常
- NumberFormatException - 数字格式异常
- SecurityException - 安全异常
- UnsupportedOperationException - 不支持的操作异常
二、Java异常机制使用的关键字
Java异常机制用到的几个关键字:try、catch、finally、throw、throws。
- try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
- finally – 不管是否有异常产生,finally块中代码都会执行。它主要用于回收在try块里打开的物理资源(如数据库连接、网络连接和磁盘文件)。只有finally块执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
- throw – 用于抛出异常。它可以用来抛出任意异常的,可以抛出任意 Throwable,包括自定义的异常类对象。
- throws – 用在方法签名中,用于声明该方法可能抛出的异常。它总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常。如果方法抛出了异常,那么调用这个方法的时候就需要处理这个异常。
三、try、catch、finally中return的执行顺序
关键点
- finally中的代码总会执行的。
- try和catch中有return语句时,finally块仍然会执行。
- 如果finally中没有return语句,try或catch中有return,返回值在finally执行前已经确定。
- 如果finally中有return语句,try或catch中也有return,执行完finally中的return后直接退出。
public class TryCatchTest {
public static void main(String args[]){
System.out.println(test1());
System.out.println(test2());
System.out.println(test3());
System.out.println(test4());
}
/**
* 先执行try块中return之前(包括return语句中的表达式运算)代码,此时x为1
* 要return的结果已经准备好了,将1存储在局部变量中
* 再执行finally块,x此时为2
* 最后执行try中return,将准备好的结果1取出来return;
* @return 1
*/
public static int test1(){
int x = 0;
try{
x++;
return x;
}
finally{
x++;
}
}
/**
* 与之前类似,先执行try中的代码,执行到y=1/x时,发现异常,则try中此处之后的代码不再执行
* 进行catch代码,为return语句,x此时为0,将需要return的结果保存起来
* 再执行finally代码,x的值此时为1
* 返回catch语句中return之前保存的0
* finally{}之后的return语句不再执行
* @return 0
*/
public static int test2(){
int x = 0;
try{
int y = 1/x;
x++;
}
catch(Exception e){
return x;
}
finally{
x++;
}
return x;
}
/**
* 首先执行try语句中的代码,其中包含return,x的值此时为1,先将其临时保存起来
* 未发现异常,执行finally语句
* finally代码中包含return,x的值此时为2,执行return语句后不再返回try中。
* @return 2
*/
public static int test3(){
int x = 0;
try{
x++;
return x;
}
catch(Exception e){
}
finally{
x++;
return x;
}
}
/**
* 程序执行try块中return之前代码,遇到y=1/x异常,不再执行其后的返回语句,此时x为0
* 有异常,执行catch块中return之前代码,此时x为0,将返回结果暂时保存起来;
* 再执行finally块,此时x为1,因为有return所以提前退出。
* @return 1
*/
public static int test4(){
int x = 0;
try{
int y = 1/x;
return x;
}
catch(Exception e){
return x;
}
finally{
x++;
return x;
}
}
}