一、Java异常的基础知识
Java中的异常用对象来表示。Java对异常的处理是按异常分类处理的,不同异常有不同的分类,每种异常都对应一个类型(class),每个异常都对应一个异常(类的)对象。
异常类有两个来源,一是Java语言本身定义的一些基本异常类型,二是用户通过继承Exception类或者其子类自己定义的异常。Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
异常的对象从哪里来呢?有两个来源,一是Java运行时环境自动抛出系统生成的异常,而不管你是否愿意捕获和处理,它总要被抛出!比如除数为0的异常。二是程序员自己抛出的异常,这个异常可以是程序员自己定义的,也可以是Java语言中定义的,用throw 关键字抛出异常,这种异常常用来向调用者汇报异常的一些信息。
异常是针对方法来说的,抛出、声明抛出、捕获和处理异常都是在方法中进行的。
Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理。
二、Java异常类类图
Java中的异常类都继承自Trowable类。一个Throwable类的对象都可以抛出(throw),在整个Java的异常结构中,有两个最常用的类,分别是Exception和Error,它们全是Throwable的子类。
Exception:一般表示的是程序中出现的问题,可以直接用try-catch处理
Error:一般指的是JVM错误,程序中无法处理。
Exception类可以分为两种:运行时异常和受检查异常。
1、运行时异常
RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。例如,当除数为零时,就会抛出java.lang.ArithmeticException异常。
2、受检查异常
除了RuntimeException类及其子类外,其他的Exception类及其子类都属于受检查异常,这种异常的特点是要么用try...catch捕获处理,要么用throws语句声明抛出,否则编译不会通过。
3、两者的区别
运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。
受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。
三、Java异常处理机制
对于可能出现异常的代码,有两种处理办法:
第一、在方法中用try...catch语句捕获并处理异常。
基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出。
try{
//有可能出现异常的语句
}catch(异常类型1 异常的变量名1){
//异常的处理语句
}catch(异常类型2 异常的变量名2){
//异常的处理语句
}finally{
//一定会运行到的程序代码
}
try语句中捕获可能出现的异常代码,如果在try语句中产生了异常,则程序会自动跳转到catch语句中找到匹配的异常类型进行相应的处理。catch语句可以有多个,用来匹配多个异常,但是在Java中所有捕获范围小的异常必须放在捕获大的异常之前,否则程序在编译时就会报错。匹配上多个中的一个后,执行catch语句块时候仅仅执行匹配上的异常。catch的类型是Java语言中定义的或者自己定义的,表示代码抛出异常的类型,异常的变量名表示抛出异常的对象的引用,如果catch捕获并匹配上了该异常,那么就可以直接用这个异常变量名,此时该异常变量名指向所匹配的异常,并且在catch代码块中可以直接引用。
最后不管程序是否产生异常,都肯定会执行到finally语句,finally语句就作为异常的统一出口。而且finally块是可以省略的,如果省略了finally块,则在catch()块运行结束后,程序会跳到try-catch块之后继续执行。
第二、对于处理不了的异常或者要转型的异常,在方法的声明处通过throws语句抛出异常,使用throws声明的方法表示此方法不处理异常,而交给方法调用处处理。
class Math{ public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理 int temp = i / j ; // 计算,但是此处有可能出现异常 return temp ; } }; public class ThrowsDemo01{ public static void main(String args[]){ Math m = new Math() ; // 实例化Math类对象 try{ System.out.println("除法操作:" + m.div(10,2)) ; }catch(Exception e){ e.printStackTrace() ; // 打印异常 } } };
在以上代码中,不管是否有问题,都要使用try-catch块进行异常的捕获与处理。既然throws是在方法处定义的,那么在主方法中也可以使用throws关键字,但是主方法为程序的起点,所以此时主方法再向上抛异常,则只能将异常抛给JVM进行处理。
class Math{ public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理 int temp = i / j ; // 计算,但是此处有可能出现异常 return temp ; } }; public class ThrowsDemo02{ // 在主方法中的所有异常都可以不使用try...catch进行处理 public static void main(String args[]) throws Exception{ Math m = new Math() ; // 实例化Math类对象 System.out.println("除法操作:" + m.div(10,0)) ; } };
以上代码在主方法处使用了throws关键字,所以在程序主方法中就不用再使用try-catch语句进行异常的捕获及处理了。
与throws关键字不同的是,可以直接使用throw关键字抛出异常,抛出时直接抛出异常类的实例化对象即可。
public class ThrowDemo01{ public static void main(String args[]){ try{ throw new Exception("自己抛出的异常。") ; // 抛出异常的实例化对象 }catch(Exception e){ //捕获异常 System.out.println(e) ; } } };
程序运行的结果:
java.lang.Exception:自己抛出的异常。
最后还应该注意一下异常处理的语法规则:
第一、try语句不能单独存在,可以和catch、finally组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。
第二、try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
第三、多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。
第四、throw语句后不允许有紧跟其他语句,因为这些没有机会执行。
第五、如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。一般来说,方法声明的时候用了throws语句,方法中有throw语句,方法调用的方法声明有throws关键字。
四、throw和throws关键字的区别
throw用来抛出一个异常,在方法体内。语法格式为:
throw 异常实例化对象
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:
public 返回值类型 方法名称(参数列表...)throws 异常类型1,异常类型2...