2.1概述

概念

  • 异常处理的抓抛模型
  • 异常类对象的生成
    • 由虚拟机自动生成,自动抛出异常
    • 由开发人员手动创建,手动抛出异常
  • 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。
    如果异常没有在调用者方法里处理,它继续被抛给这个调用方法的上层方法。
    这个过程将一起继续下去,直到异常被。这个过程被称为捕获异常(catch)
  • 如果一个异常回到main()方法,并且main()也不处理,则程序运行终止
  • 程序员通常只能处理Exception,而对Error无能为力

Java异常处理的五个关键字:try、catch、finally、throw、throws

如何处理异常

  • 抛—执行代码时,一旦出现异常,就会在异常的代码处理生成一个应对的异常类的对象,并将此对象抛出(分为自动抛出、手动抛出)
    • 一旦抛出此异常类的对象,程序就终止执行
    • 此异常类的对象抛给方法的调用者(java运行时系统)
  • 抓—抓住上一步抛出的异常类的对象。如何抓取
  • 处理方式
    • 异常处理方式一:抓取异常(捕获异常)try-catch-finally
    • 异常处理方式二:声明抛出异常throws

2.2处理方式一:抓取异常

try-catch-finally

  1. try {
  2. // 可能出现异常的代码
  3. } catch(Exception1 e) {
  4. // 异常1处理方法
  5. } catch(Exception2 e) {
  6. // 异常2处理方法
  7. } catch(Exception3 | Exception4 | Exception5 e) {
  8. // 异常2处理方法
  9. e.printStackTrace();
  10. } finally {
  11. // 一定要执行的代码
  12. }

注意

  • finally是可选的
  • try 块内声明的变量为局部变量。出了try { }就不能被调用了
  • catch 语句内对异常的处理
    • getMessage() — 返回String关键错误信息
    • printStackTrace() — 打印异常类名和异常信息,以及异常出现在程序中的位置。返回值void
  • 可以多个catch语句,try中抛出的异常类对象从上往下匹配catch中的异常类的类型,一旦匹配就执行catch中的代码 执行完就跳出后面的catch语句
  • 如果异常处理了,其后的代码继续执行
  • 对于运行时异常,可以不显式的进行处理;对于编译时异常,必须要显式的进行处理
  • 若catch中多个异常类型是”并列”关系,哪个在上都可以 若catch中多个异常类型是”包含”关系,必须子类异常类放在父类异常类的上面进行处理,否则编译报错。
  • finally中的语句一定会被执行。不管try、catch中是否仍有异常未被处理,以及是否有return语句,除了是明确指定退出程序外,如System.exit(1)
  • try-catch可以嵌套
  • 一个catch抓取多个异常时,多个异常类型之间用 “|” 分隔
  • Exception1 - Exception5:异常对应的类

2.3处理方式二:声明抛出异常

当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。throws 具体格式如下:

returnType method_name(paramList) throws Exception 1,Exception2,…{…}

其中,returnType 表示返回值类型;method_name 表示方法名;paramList 表示参数列表;Exception 1,Exception2,… 表示异常类。

如果有多个异常类,它们之间用逗号分隔。这些异常类可以是方法中调用了可能拋出异常的方法而产生的异常,也可以是方法体中生成并拋出的异常。

使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。

示例 Throws Exception

手动抛出异常throw

在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。

在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?

  1. 创建一个异常对象。封装一些提示信息(信息可以自己编写)。
  2. 需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw 异常对象。
    throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

使用格式:

throw new 异常类名(参数);

例如:

throw new NullPointerException("要访问的arr数组不存在");

throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

学习完抛出异常的格式后,我们通过下面程序演示下throw的使用。

public class ThrowDemo {
    public static void main(String[] args) {
        //创建一个数组 
        int[] arr = {2,4,52,2};
        //根据索引找对应的元素 
        int index = 4;
        int element = getElement(arr, index);

        System.out.println(element);
        System.out.println("over");
    }
    /*
     * 根据 索引找到数组中对应的元素
     */
    public static int getElement(int[] arr,int index){ 
           //判断  索引是否越界
        if(index<0 || index>arr.length-1){
             /*
             判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
             这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 
              */
             throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
        }
        int element = arr[index];
        return element;
    }
}

注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。

那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。

声明异常throws

声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

声明异常格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }

声明异常的代码演示:

public class ThrowsDemo {
    public static void main(String[] args) throws FileNotFoundException {
        read("a.txt");
    }

    // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
    public static void read(String path) throws FileNotFoundException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
    }
}

throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

public class ThrowsDemo2 {
    public static void main(String[] args) throws IOException {
        read("a.txt");
    }

    public static void read(String path)throws FileNotFoundException, IOException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
        if (!path.equals("b.txt")) {
            throw new IOException();
        }
    }
}

2.4 异常注意事项

  • 多个异常使用捕获又该如何处理呢?
    1. 多个异常分别处理。
    2. 多个异常一次捕获,多次处理。
    3. 多个异常一次捕获一次处理。


一般我们是使用一次捕获多次处理方式,格式如下:

try{
     编写可能会出现异常的代码
}catch(异常类型A  e){  当try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){  当try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
  • 如果finally有return语句,永远返回finally中的结果,避免该情况.
  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
  • 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出