Logo

郎哥编程

捕获和处理异常

2021-07-03 259

学习目标:使用try和catch语句捕获异常,使用finally语句完善异常处理过程。

try和catch语句

Java程序在执行过程中如果出现异常,会自动生成一个异常对象,该异常对象将被自动提交给JVM,当JVM接收到异常对象时,会寻找能处理这一异常的代码,并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。如果JVM找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。

在java中添加捕获异常代码时,需要把认为可能会出现异常的代码包括在try语句块内,处理异常的代码包括在catch语句块内。在程序执行时,如果try语句内的代码出现错误,try会创建异常对象并抛出,catch捕获异常对象,catch语句块内的代码将会执行,这样就可以处理异常错误了。

使用try和catch的语法规则如下:

try {
   可能发生异常的代码
}
catch(异常类型  ex) {
   对异常进行处理的代码
}

案例4:建立一个测试类,在main()方法中使用try-catch语句捕获和处理异常。

在excep包下新建TryTest类。代码如下:

import java.util.Scanner;
 
public class TryTest {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int number = -1;
        Scanner in = new Scanner(System.in);
        try {
            number = Integer.parseInt(in.nextLine());
            //如果存在异常,下面这行代码是不会输出的
            System.out.println("程序没有发生异常");
            System.out.println("您输入的数字为:" + number);
        } catch(Exception e) {
            System.out.println("非法的数字");
        }
        System.out.println("程序运行结束");
 
    }
}

上面的代码通过Scanner获取用户输入的字符串,然后将字符串转换为整数。如果用户输入不是数字型字符串,转换过程将会发生异常,发生的异常会被catch内的语句块处理。

通过案例可以发现,当程序没有异常时,catch内的语句不会被执行。当程序有异常时,如果被try语句捕获到,将会执行catch内的语句,catch后面的代码也会被执行。如果异常没有被try语句捕获到,异常会向上抛出,即返回调用当前代码的位置,如果该位置未对异常进行处理,则继续向上抛出,直至被JVM捕获异常,JVM会停止程序的运行。

前面介绍的是处理单个异常,有时代码可能会引发多个异常,在这种情况下,可以定义两个或更多的catch子句,每个子句捕获一种类型的异常,处理多个异常时,一般是子类异常在前,父类异常在后。

多重catch语句的语法规则如下:

try {
   ……….  //代码段
   ……….  //产生异常
}
catch(异常类型1  ex) {
   ………. //对异常1进行处理代码段
} catch(异常类型2  ex) {
   ………. //对异常2进行处理代码段
}
} catch(异常类型n  ex) {
   ………. //对异常n进行处理代码段
}

案例5:建立MultiCatchTest类,进行多异常处理。

在excep包下新建MultiCatchTest类。代码如下:

package excep;
 
public class MultiCatchTest {
    public static void main(String[] args) {
        try {
            // 获取args数组长度
            int a = args.length;
            // 输出args数组长度
            System.out.println("args数组长度为:" + a);
            //执行除法操作
            int b = 42 / a;
            // 定义c数组,从数组的长度为1
            int c[] = { 1 };
            // 给c数组第9个元素赋值
            c[10] = 99;
        } catch (ArithmeticException e) {
            System.out.println("Divide by 0: " + e);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Array index oob: " + e);
        }
        System.out.println("After try/catch blocks.");
 
    }
}

代码在没有命令行输入参数条件下运行导致被零除异常,因为a为0。如果输入一个命令行参数,把a设成大于零的数值,就不会出现被零除的异常。但是它将导致ArrayIndexOutOf BoundsException异常,因为整型数组c的长度为1,而程序试图给c[10]赋值。

从案例5可以看出,一段代码中可能会产生多种不同的异常,可以设置多个异常抛出点来解决这个问题。

异常对象从被抛出到捕捉,实际上是一个执行控制权转移的过程,所以可以根据程序需要合理地控制检测到异常的粒度。如果并不需要知道具体产生的是什么异常,那么可以使用异常的公共父类Exception来处理异常对象,即catch(Exception e){…} 。

finally语句

如果try语句块中存在异常,则异常之后的代码将不再执行。但在某些特定的情况下,不管是否有异常发生,总是要求某些特定的代码必须被执行。例如,程序中执行数据库连接的代码,不管对数据库的操作是否成功,最后都要关闭数据库的连接以释放内存资源,这就需要用到finally语句。

finally语句的语法规则如下:

try {
    //代码段(可能发生异常的代码)
} catch( 异常类型 ex ) {
    //对异常进行处理的代码
} finally {
   //总要被执行的代码
}
//代码段

 案例6:建立FinallyDemo类,演示finally的用法。

在execp包下新建FinallyDemo类。代码如下:

package excep;
 
public class FinallyDemo {
    public static void main(String[] args) {
        try {
            methodA();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        methodB();
 
    }
 
    static void methodA() {
        try {
            System.out.println("进入方法A");
            //抛出异常
            throw new RuntimeException("制造异常");
        } finally {
            //该语句不管发生什么都会执行
            System.out.println("执行A方法的finally");
        }
    }
 
    static void methodB() {
        try {
            System.out.println("进入方法B");
            //返回,实际上是在finally语句执行完后才返回
            return;
        } finally {
            //该语句不管发生什么都会执行
            System.out.println("执行B方法的finally");
        }
    }
 
}

运行程序,输出结果如下图所示:

09.png

从输出结果可以看出,不管什么情况下,finally语句都会被执行。

在代码methodB方法try语句块中,程序执行到return语句时,程序会产生一个局部展开,finally语句块的代码会被插入到return语句之前执行。不过注意的是,finally语句块的代码虽然在return语句之前执行,但是finally语句块的代码不能够通过重新赋值的方式来改变return语句的返回值。

案例7:建立TestFinallyReturn类,测试是否能通过finally语句来改变return语句的返回值。

在excep包下新建TestFinallyReturn类。代码如下:

package excep;
 
public class TestFinallyReturn {
    public static void main(String[] args) {
        //你认为testFinally方法返回的值是多少呢?
        System.out.println("testFinally返回的值为:" + testFinally());
    }
 
    public static int testFinally()
    {
        int temp = 1;
        try {
            // 返回temp的值
            return temp;
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            // temp赋值为2
            temp = 2;
        }
        return 0;
    }
 
}

案例代码中,本来是想在finally语句块中通过改变temp的值,来影响testFinally方法最终返回值,但是真的影响了吗?程序输出结果如下图所示:

10.png

从上图可以看出,testFinally方法并没有返回我们需要的值。虽然finally语句块的代码不会影响try语句块中return语句的返回值。但是可以在finally内部使用return语句。如果把上列的finally语句块的代码改为如下所示:

public static int testFinally()
{
    int temp = 1;
    try {
        return temp;
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        temp = 2;
        return temp;
    }
    return 0;
}

由于finally语句块的代码优于return语句执行,如果此时在finally语句块中内部也有return语句,这将会导致该方法直接返回,而使try语句块中的return语句得不到执行机会,所以返回结果为2。

对上述情况,其实更合理的做法是,既不在try语句块中使用return语句,也不在finally语句块中使用return语句,而应该在finally语句块之后使用return语句来表示方法的结束和返回。修改后的代码如下:

public static int testFinally()
{
    int temp = 1;
    try {
        //操作语句
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        temp = 2;
    }
    return temp;
}

  finally语句一般和try......catch......连用,用来做一些善后清理工作,可以把文件关闭,关闭数据库连接等操作放置到finally语句块内,避免程序出现异常时,不能及时执行关闭文件等释放系统资源操作。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论