本次学习的目标
了解异常的基本概念
掌握异常的基本处理格式
掌握异常类的继承结构
掌握java的异常处理机制
具体内容
异常
异常是导致程序中断运行的一种指令流,如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失,,所以在程序的设计中必须要考虑各种异常的发生,并正确的做好相应的处理,这样才能保证程序正常的执行。
1.为什么需要异常处理
首先我们来认识一下异常。我们新建项目ExceptionDemo,
新建类ExceptionDemo01,通过简单的算术运算来看一下
publicclassExceptionDemo01{
publicstaticvoidmain(String args[]){
inti = 10 ;//定义整型变量
intj = 2 ;//定义整型变量
inttemp = i / j ;//两数相除
}
}
运行结果:
运行结果我可以看到是正确的,那么如果我们将j赋值为0,因为我们知道分母不能为0,为零就会出现错误。
我们来修改试一下。
publicclassExceptionDemo01{
publicstaticvoidmain(String args[]){
inti = 10 ;//定义整型变量
intj =;//定义整型变量
inttemp = i / j ;//两数相除
}
}
运行结果:
可以看到这里报错了,报了一个ArithmeticException错误,也就是算术异常,异常后面的语句都没有执行。
一旦产生异常之后,异常之后的语句并不会执行,而是直接结束程序,并将错误报告给用户。
2.处理异常
如果要想处理异常,则必须掌握异常的处理格式。
异常的处理格式
格式:try{//有可能出现的异常
}catch(异常类 异常对象){
//编写异常的处理语句
}catch(异常类 异常对象){
//编写异常的处理语句
}......
finally{
//一定会运行到的程序代码
}
对异常进行捕捉
新建ExceptionDemo02
publicclassExceptionDemo02{
publicstaticvoidmain(String args[]){
inti = 10 ;//定义整型变量
intj = 0 ;//定义整型变量
try{
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){
}
}
}
运行结果:
可以看到跟ExceptionDemo01不同的时,这里我们对异常进行了捕捉,输出的异常是一样的算术异常,但是后面的结束语句有执行输出了。
因为我们知道是算术异常,所以在catch中填入的是ArithmeticExceptione
程序的执行流程是这样的。
以上的操作代码中只使用了基本的异常处理格式:try...catch,try中捕获异常,出现异常之后的代码将不再被执行,而是跳转到相应的catch运距中执行,用于处理异常。
当然,对于异常也可以设置其统一的出口,使用finally完成。
新建ExceptionDemo03
publicclassExceptionDemo03{
publicstaticvoidmain(String args[]){
inti = 10 ;//定义整型变量
intj = 0 ;//定义整型变量
try{
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){//捕获算术异常
}finally{//作为异常的统一出口
}
}
}
再用intj =2;执行
可以看到不管是否出现异常,都会执行finally里面的代码。
在以上的程序中,只是对代码中处理了一个异常,如果现在有多个异常呢?
我们修改一下代码,新建ExceptionDemo04
publicclassExceptionDemo04{
publicstaticvoidmain(String args[]){
inti = 0 ;//定义整型变量
intj = 0 ;//定义整型变量
try{
String str1= args[0] ;//接收第一个参数
String str2= args[1] ;//接收第二个参数
i =Integer.parseInt(str1) ;//将第一个参数由字符串变为整型
j =Integer.parseInt(str2) ;//将第二个参数由字符串变为整型
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){//捕获算术异常
}
}
}
此时的数据实际上是交给用户输入的,那么既然是用户输入,就有可能输入不正确,实际上以上的代码就可能出现三个问题。
在Eclipse中,需要输入参数就要右击你的代码 ---->【Run as】---->【Run configurations】--->【JavaApplication】--->【arguments】:然后在Program arguments里输入参数即可,当然也可以用Scanner类的方法从键盘获取输入,
1、如果没有输入参数或者输入的参数不够,则会出现问题
此时,就会出现数组超出绑定,因为我们没有输入参数。
下面用Scanner类的方法来获取输入,我们修改一下代码,
importjava.util.Scanner;
//导入支持类(可以是JDK基础类或者自己编写的类),可以供本类调用方法和属性。 这里导入的是Scanner类
publicclassExceptionDemo04{
publicstaticvoidmain(String args[]){
inti = 0;//定义整型变量
intj = 0;//定义整型变量
try{
Scanner sc =newScanner(System.in);//Scanner类,获取键盘输入
i = sc.nextInt();//nextInt()代表输入的是整型。输入i
j = sc.nextInt();//nextInt()代表输入的是整型。输入j
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){//捕获算术异常
}
}
}
这里有一个Scanner类,我们可以查看一下JDK的帮助文档,属于java.util
可以看到是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。我们这里用来获取键盘输入。
2、如果现在输入的时候输入的参数不是数字,而是字母
这里就出现了另一个异常,因为我们获取的是Int类型,而与我们输入的不匹配。
3、算术异常,如果被除数为0的话
在看一下正确的输入:
所以,上面的程序实际上产生了三个比较明显的异常:
数组超出绑定:ArrayIndexOutOfBoundsException
类型不匹配抛出:InputMismatchException
算术异常:ArithmeticException
如果要想保证程序的执行正确,则必须同时对三个异常进行处理,所以此时就需要三个catch语句。
新建ExceptionDemo05
importjava.util.InputMismatchException;
importjava.util.Scanner;
//import:导入支持类(可以是JDK基础类或者自己编写的类),可以供本类调用方法和属性。
publicclassExceptionDemo05{
publicstaticvoidmain(String args[]){
inti = 0;//定义整型变量
intj = 0;//定义整型变量
try{
Scanner sc =newScanner(System.in);//Scanner类,获取键盘输入
i = sc.nextInt();//nextInt()代表输入的是整型。输入i
j = sc.nextInt();//nextInt()代表输入的是整型。输入j
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){//捕获算术异常
//System.out.println("算术异常:" +e) ;
e.printStackTrace();
}catch(InputMismatchException e){//类型不匹配异常
}catch(ArrayIndexOutOfBoundsExceptione){//捕获数组越界异常
}
}
}
执行之后:
算术异常:
类型不匹配异常
数组越界异常:
我们现在一个小小的程序都要处理三个异常,当然,这个程序还有可能有其他的异常,那么这样一来,异常的处理就很麻烦。
3.异常类的继承结构
从之前的三个异常:ArrayIndexOutOfBoundsException、InputMismatchException、ArithmeticException
在整个java的异常结构中,实际上有一下两个最常用的类:Exception、Error,这两个类全都是Throwable的子类。
Exception:一般表示的是程序中出现的问题,可以直接使用try...catch处理
Error:一般指的是JVM错误,程序无法处理。
比如:
程序无法执行,这样的错误就是属于Error。
一般情况下我们都比较习惯于将Error和Exception统一称为异常,而之前处理的异常,都是Exception的子类。
注意:
在ExceptionDemo05中
换成
e.printStackTrace() ;
运行的结果是一样的。
4.Java的异常处理机制
在整个java的异常处理中,实际上也是按照面向对象的方式进行处理,处理的步骤如下:
一旦产生异常,则首先会产生一个异常类的实例化对象;
在try语句中对此异常对象进行捕捉;
产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功,则执行catch语句中的代码。
之前我们学习过:子类的实例化对象可以直接使用父类的对象进行接收。
那么异常处理中呢?实际上也是使用伺候总概念,因为try中产生的是一个实例化对象。如果现在又一些其他的异常无法知道的话,则可以最后使用Exception进行捕获。
新建ExceptionDemo06
importjava.util.InputMismatchException;
importjava.util.Scanner;
//import:导入支持类(可以是JDK基础类或者自己编写的类),可以供本类调用方法和属性。
publicclassExceptionDemo06{
publicstaticvoidmain(String args[]){
inti = 0;//定义整型变量
intj = 0;//定义整型变量
try{
Scanner sc =newScanner(System.in);//Scanner类,获取键盘输入
i = sc.nextInt();//nextInt()代表输入的是整型。输入i
j = sc.nextInt();//nextInt()代表输入的是整型。输入j
inttemp = i / j ;//此处产生了异常
}catch(ArithmeticException e){//捕获算术异常
//System.out.println("算术异常:" +e) ;
e.printStackTrace();
}catch(InputMismatchException e){//类型不匹配抛出
}catch(ArrayIndexOutOfBoundsExceptione){//捕获数组越界异常
}catch(Exception e){
}
}
}
注意点:
在异常处理中,捕获更粗的异常要放在捕获更细的异常之后,也就是范围大的在后。我们现在将粗的放在前面,执行一下。
这里显示后面的三个异常已经被捕获了。
因为前面的是粗的,后面的是细的,程序是顺序执行的,前面已经被捕获了,后面就不会再执行了。
那么现在又有疑问了,既然所有的Exception对象都可以使用Exception接收,那么直接使用Exception捕获不是更方便吗?
我们新建ExceptionDemo07
publicclassExceptionDemo07{
publicstaticvoidmain(String args[]){
inti = 0 ;//定义整型变量
intj = 0 ;//定义整型变量
try{
String str1= args[0] ;//接收第一个参数
String str2= args[1] ;//接收第二个参数
i =Integer.parseInt(str1) ;//将第一个参数由字符串变为整型
j =Integer.parseInt(str2) ;//将第二个参数由字符串变为整型
inttemp = i / j ;//此处产生了异常
}catch(Exception e){
}
}
}
三种异常都是可以捕获的,
当所有的异常处理的方式是一样的时候,就可以使用以上的形式,直接使用Exception进行捕获。当然,在一个比较细致的开发中是不建议这样使用能够的们所有的异常最好分别捕获。
既然我们是用Exception这么方便,那么直接使用Throwable可不可以呢?
首先是可以的,但是正常的开发过程中是不会这样做的,因为在程序try的永远只会抛出Exception的子类,而Throwable中不光有Exception还有Error。
总结
1、异常出现之后,如果没有合理的处理的话,则会让整个程序中断执行
2、使用try...catch和try...catch...finally可以处理异常,finally将作为异常的统一出口,不管是否有异常都会执行此语句。
3、一个异常处理中可以同时出现多个catch,但是捕获更粗的异常要放在捕获更细的异常之后,否则程序编译的时候将出现错误。
4、在异常中最大的类是Throwable,分为两个子类:Exception、Error
a)Exception:是可以自己处理的异常
b)Error:一般指的是JVM错误,程序无法处理。
5、捕获的时候可以直接捕获Exception,但是最好分开捕获,如果所有的异常处理操作是一样的话,则也可以直接捕获Exception。
每当异常产生后,会在程序中产生一个异常类的实例化对象,之后使用能够此对象与catch中的异常类型相匹配,如果匹配成功,则执行catch语句的内容,如果匹配不成功,则向下继续匹配,如果都无法成功,程序就会出现中断执行的问题。
关爱IT人,关注挨踢栏
领取专属 10元无门槛券
私享最新 技术干货