欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java异常处理机制

程序员文章站 2024-03-23 21:06:10
...

一.Java中的异常类概述

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语句块后的语句

Java异常处理机制

可以看一段实例如下:

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();
     }
 }

程序运行结果如下:

Java异常处理机制

有两种特殊情况需要注意:

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虚拟机)。因此执行结果如下:

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中的输出语句,然后再返回计算结果,因此代码的执行结果如下:

Java异常处理机制

如果在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

Java异常处理机制

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版)