Java异常处理机制
一.Java中的异常类概述
Java将程序中出现的异常情况封装成对象,用对象实例对异常情况进行描述。
Java异常类层次结构图:
从图中可以看出,所有的异常都是由Throwable继承而来,Throwable有两个直接子类:Error和Exception。
Error是程序无法处理的错误,一般是Java运行时系统的内部错误和资源耗尽错误。例如Java虚拟机错误。出现这类错误,会导致程序的终止,无法对这类错误进行处理。
Exception是程序可以处理的异常。它又可以分为两类,一类是派生于RuntimeException的异常,另一类是其他异常,非RuntimeException异常。RuntimeException类型的异常是由程序自身错误导致的异常,如NullPointerException(空指针异常)、ArrayIndexOutOfBoundException(数组角标越界异常)等,如果出现RuntimeException,那么一定是程序逻辑有问题。其他异常,即非RuntimeException是程序本身没问题,由外部环境错误导致的异常。如打开一个不存在的文件导致的FileNotFoundException。
Java语言规范将派生于Error类或RuntimeException类的异常称为非受查(unchecked)异常,除这两类之外的所有异常称为受查(checked)异常。对于非受查异常,程序可以不用处理,应该在书写程序的时候,尽量避免这类错误情况的发生。受查异常是程序必须处理的异常,要么抛出,要么捕获,如果不处理,程序就不能编译通过。
二.异常处理机制
Java应用程序中,异常的处理机制有两种:抛出异常和捕获异常。
1.抛出异常
对于方法中可能出现的异常,方法本身不做处理,而是使用throws关键字声明方法中可能出现的异常,当异常发生时,方法会抛出一个异常对象,由方法的调用者对异常对象进行处理,调用者可以对异常进行捕获处理,也可以继续抛给它的调用者。
2.捕获异常
使用try-catch-finally语句块进行异常的捕获处理。
try{
//可能发生异常的程序代码
}
catch(ExceptionType1 e1){
//捕获ExceptionType1类型的异常,并进行处理
}
catch(ExceptionType2 e2){
//捕获ExceptionType2类型的异常,并进行处理
}
//可以有多个catch语句块
finally{
//无论是否发生异常,都会被执行的语句块
//也可以没有finally语句块,只有try和catch
}
关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。Java方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之 外,由Java运行时系统试图寻找匹配的catch子句以捕获异常。若有匹配的catch子句,则运行其异常处理代码,try-catch语句结束。
匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。
当有多个catch语句块时,一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着try-catch语句结束,其他的catch块不再有匹配和捕获异常类型的机会。
try-catch-finally语句块的执行顺序:
1)当try没有抛出异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句
2)当try抛出异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行
3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句
可以看一段实例如下:
public class ExceptionTest
{
public void method()
{
try
{
System.out.println("进入到try块");
}
catch (Exception e)
{
System.out.println("异常发生了!");
}
finally
{
System.out.println("进入到finally块");
}
System.out.println("后续代码");
}
public static void main(String[] args)
{
ExceptionTest test = new ExceptionTest();
test.method();
}
}
程序运行结果如下:
有两种特殊情况需要注意:
1)当在catch块中处理捕获到的异常时,如果此时catch块中又抛出一个异常,异常抛出后,finally语句块中的代码会被执行,finally中的代码执行完成后,finally语句块后面的其他代码不会被执行,catch中抛出的异常会被抛给方法的调用者。
实例如下:
public class ExceptionTest {
public static void main(String[] args) {
try{
//这行代码会抛出异常
int i = 100 / 0;
System.out.println("try代码块");
}catch(Exception e){
System.out.println("catch代码块1");
throw new RuntimeException();
}finally{
System.out.println("finally代码块");
}
System.out.println("后续代码块");
}
}
由于catch在处理异常时,又抛出了一个异常,因此finally中的代码执行完毕后,后续代码不会被执行,而是抛出一个异常给方法调用者(这里是Java虚拟机)。因此执行结果如下:
2)当try或catch语句块中有return语句时,finally语句块中的代码会在return语句之前执行,即先执行finally中的代码,再执行try或catch中的return,(PS:JVM对这种代码的处理方式是先将返回值复制一份存到本地变量表中,腾出操作数栈用来执行finally语句块中代码,等finally执行完毕,再将暂存的本地变量表中的值复制回操作数栈顶。所以无论finally语句块中执行了什么操作,都无法影响返回值。)如果此时finally语句块中也有return语句,那么finally中的return会覆盖try或catch中的return,即finally中的return会被执行,而try或catch中的return不会执行。
实例如下:
public class ExceptionTest{
public int calculate(int a,int b){
try {
System.out.println("try语句块");
return a+b;
}
catch (Exception e) {
System.out.println("catch语句块");
}
finally{
System.out.println("finally语句块");
}
return 0;
}
public static void main(String argv[]){
ExceptionTest et1 =new ExceptionTest();
System.out.println("计算结果是:"+et1.calculate(10, 5));
}
}
这段程序中try中的return语句执行之前,会先执行finally中的输出语句,然后再返回计算结果,因此代码的执行结果如下:
如果在finally语句块中也加上return语句
public class ExceptionTest{
public int calculate(int a,int b){
try {
System.out.println("try语句块");
return a+b;
}
catch (Exception e) {
System.out.println("catch语句块");
}
finally{
System.out.println("finally语句块");
//try和finally中都有return
return a-b;
}
}
public static void main(String argv[]){
ExceptionTest et1 =new ExceptionTest();
System.out.println("计算结果是:"+et1.calculate(10, 5));
}
}
此时try中的return不会被执行,而是执行finally中的return,因此计算结果是a-b,而不是a+b
public class ClassFileStructure {
public static int inc(){
int x;
try{
x = 1;
return x;
}catch (Exception e){
x = 2;
return x;
}
finally {
x = 3;
}
}
public static void main(String args[]){
System.out.println(inc());
}
}
上述代码中,在finally语句块中修改x的值为3,但最终返回的x值是1而不是3,原因就是因为JVM的执行逻辑是先将返回值(这里是1)复制到本地变量表中,然后去执行finally中的代码,虽然finally中将x的值修改为3,但最终返回的是之前复制到本地变量表中x值,而不是修改后的x值。
参考资料:
1.https://blog.csdn.net/hguisu/article/details/6155636
2.Java核心技术卷〡基础知识(原书第10版)
上一篇: SQL——分组比较
下一篇: Java异常处理机制
推荐阅读
-
Java异常处理机制
-
Java异常处理机制
-
java异常处理机制
-
java异常处理的(try catch或throws): 博客分类: Java EE java
-
java异常处理的(try catch或throws): 博客分类: Java EE java
-
在CDH3上运行mahout的random forest算法时的异常处理 博客分类: mahout
-
在CDH3上运行mahout的random forest算法时的异常处理 博客分类: mahout
-
Tomcat7-Tomcat8的manager配置以及在idea下的异常表现 博客分类: Java_about
-
ORACLE 11 ORA-00119 ORA-00132启动异常抛错处理 博客分类: linux
-
ORACLE 11 ORA-00119 ORA-00132启动异常抛错处理 博客分类: linux