初识Java异常处理
异常是程序之中导致程序中断的一种指令流,下面,通过两个程序来进行异常产生问题的对比。
范例:不产生异常的程序
1、除法计算开始。
2、除法计算结果:5
3、除法计算结束。
范例:产生异常的程序
1、除法计算开始。
Exception in thread “main” java.lang.ArithmeticException: / by zero
at TestDemo.main(TestDemo.java:4)
可以发现,加入了异常处理之后,程序之中即使有了异常,程序也可以正常的执行完毕,但是现在发现,异常处理时的错误输出信息和之前相比,出错的信息不明确了,那么为了让错误的信息更加的完整,一般而言,都会调用printStackTrace()方法进行异常信息的打印,这个方法打印的异常信息是最完整的:
java.lang.ArithmeticException: / by zero
at TestDemo.main(TestDemo.java:5)
try {
可能出现异常的语句 ;
} [ catch (异常类型 异常对象) {
处理异常 ;
} catch (异常类型 异常对象) {
处理异常 ;
} … ] [finally {
不管是否出现异常,都执行此代码 ;
}]
范例:加入finally
但是,对于之前的程序现在又有了问题:现在执行数学计算的两个参数,都是由程序默认提供,那么如果说现在两个计算的参数通过初始化参数传递呢?
这个时候,数据由外部传送,那么在这种情况下,就有可能出现以下几类问题:
· 执行时不输入参数(java TestDemo),ArrayIndexOutOfBoundsException,未处理;
· 输入的参数不是数字(java TestDemo a b),NumberFormatException,未处理;
· 被除数为0(java TestDemo 10 0),ArithmeticException,已处理。
可以发现,以上的程序实际上是存在三种异常,而程序之中只能够处理一种,而对于不能处理的异常,发现程序依然会直接中断执行。
其他异常
public class ExceptionDemo06{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 0 ; // 定义整型变量
int j = 0 ; // 定义整型变量
try{
String str1 = args[0] ; // 接收第一个参数
String str2 = args[1] ; // 接收第二个参数
i = Integer.parseInt(str1) ; // 将第一个参数由字符串变为整型
j = Integer.parseInt(str2) ; // 将第二个参数由字符串变为整型
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;
System.out.println("----------------------------") ;
}catch(ArithmeticException e){ // 捕获算术异常
// System.out.println("算术异常:" + e) ;
e.printStackTrace() ;
}catch(NumberFormatException e){ // 捕获数字转换异常
System.out.println("数字转换异常:" + e);
}catch(ArrayIndexOutOfBoundsException e){ // 捕获数组越界异常
System.out.println("数组越界异常:" + e) ;
}catch(Exception e){
System.out.println("其他异常:" + e) ;
}
System.out.println("********** 计算结束 ***********") ;
}
};
以上已经完成了异常的基本处理流程,但是也可以发现问题,所有的异常都像之前那样一条条的判断似乎是一件不可能完成的任务,因为日后肯定会接触到一些不常见的异常信息,那么下面就必须首先研究异常的流程和结构。
先查看两个异常类的继承结构:
ArithmeticException:
java.lang.Object
|- java.lang.Throwable
|- java.lang.Exception
|- java.lang.RuntimeException
|- java.lang.ArithmeticException
ArrayIndexOutOfBoundsException:
java.lang.Object
|- java.lang.Throwable
|- java.lang.Exception
|- java.lang.RuntimeException
|- java.lang.IndexOutOfBoundsException
|- java.lang.ArrayIndexOutOfBoundsException
可以发现所有的异常类型最高的继承类是Throwable,通过doc文档可以发现在Throwable下有两个子类:
(面试题:请解释Error和Exception的区别?)
· Error:指的是JVM错误,这个时候的程序并没有执行,无法处理;
· Exception:指的是程序之中出现的错误信息,可以进行异常处理,主要关心Exception。
那么通过继承关系可以发现,肯定在进行日后异常处理的时候是以Exception为主,而这个时候就可以形成以下的异常处理流程(面试题:请解释java之中的异常处理流程。)
1、 如果程序之中产生了异常,那么会自动的由JVM根据异常的类型,实例化一个指定异常类的对象;
2、 如果这个时候程序之中没有任何的异常处理操作,则这个异常类的实例化对象将交给JVM进行处理,而JVM的默认处理方式就是进行异常信息的输出,而后中断程序执行;
3、 如果程序之中存在了异常处理,则会由try语句捕获产生的异常类对象;
4、 与try之后的每一个catch进行匹配,如果匹配成功,则使用指定的catch进行处理,如果没有匹配成功,则向后面的catch继续匹配,如果没有任何的catch匹配成功,则这个时候将交给JVM执行默认处理;
5、 不管是否有异常都会执行finally程序,如果此时没有异常,执行完finally,则会继续执行程序之中的其他代码,如果此时有异常没有能够处理(没有一个catch可以满足),那么也会执行finally,但是执行完finally之后,将默认交给JVM进行异常的信息输出,并且程序中断;
通过以上的分析,可以发现,实际上catch捕获异常类型的操作,就和方法接收参数是一样的,那么按照之前所学习过的对象多态性来讲,所有的异常类都是Exception的子类,那么这个时候,实际上所有的异常都可以使用Exception进行接收。
那么这个时候就应该可以感受到异常处理所带来的好处了。但是这种操作也存在一种问题。如果在一些异常处理要求严格的项目之中,异常必须分别处理,如果现在异常的处理要求不是很严格,直接编写Exception就足够了。
捕获大的异常包含小的异常