Java讲课笔记18:异常处理

零、本讲学习目标

1、理解什么是异常

2、熟悉异常的类型

3、掌握try…catch和finally异常处理

4、掌握throws关键字

一、什么是异常

1、程序异常及解决方法

(1)生活中的异常

在学习、工作与生活中,我们也经常会遇到一些非正常情况,例如电脑蓝屏、死机,感冒发烧,车票丢失等等。

(2)程序中的异常

在程序运行的过程中,也会发生这种非正常状况,例如程序运行时磁盘空间不足、网络连接中断、被加载的类不存在等。

在《Java讲课笔记08:数组》里,我们遇到过数组下标越界异常:
在这里插入图片描述
异常提示信息非常明确,指出程序中出现了数组下标越界异常。由于程序中第11行给不存在的数组元素scores[4]赋值,导致错误。这个异常发生后,程序会立即结束,无法继续向下执行,比如第12行的输出语句就没有被执行。

(3)程序异常解决方法

针对程序中的非正常情况,Java语言中引入了异常,以异常类的形式对这些非正常情况进行封装,并通过异常处理机制对程序运行时发生的各种问题进行处理。

2、Java异常体系结构

在这里插入图片描述
Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。

(1)Exception —— 异常

Exception称为异常类,它表示程序本身可以处理的错误。在Java程序开发中进行的异常处理,都是针对Exception类及其子类的。

(2)Error —— 错误

Error称为错误,表示Java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的,例如系统崩溃,虚拟机错误等。

3、Throwable类中的常用方法

  • 利用IntelliJ IDEA查看Throwable类的结构
    在这里插入图片描述
  • Throwable类中的常用方法
方法声明 功能描述
String getMessage() 返回此throwable的详细消息字符串
void printStackTrace() 将此throwable及其追踪输出至标准错误流
void printStackTrace(PrintStream s) 将此throwable及其追踪输出到指定的输出流

二、异常的类型

1、编译时异常

(1)编译时异常定义

在程序编译时期产生的异常,而这些异常必须要进行处理,也称为checked异常

(2)编译时异常类汇总

在Exception的子类中,除了RuntimeException类及其子类外,其他子类都是编译时异常。比如IOException(EOFException、FileNotFoundException)、SQLException。

(3)编译时异常的特点

在程序编写过程中,Java编译器就会对编写的代码进行检查,如果出现比较明显的异常就必须对异常进行处理,否则程序无法通过编译。

(4)处理编译时异常的方式

  • 使用try…catch语句对异常进行捕获处理
  • 使用throws关键字声明抛出异常,让调用者处理

2、运行时异常

(1)运行时异常定义

这种异常即使不编写异常处理代码,依然可以通过编译,也称为unchecked异常

(2)运行时异常类汇总

RuntimeException类及其子类都是运行时异常。
在这里插入图片描述

  • Java常见运行时异常类
异常类名称 异常类说明
ArithmeticException 算术异常
IndexOutOfBoundsException 下标越界异常
ClassCastException 类型转换异常
NullPointerException 空指针异常
NumberFormatException 数字格式化异常

(3)运行时异常的特点

运行时异常是在程序运行时由Java虚拟机自动进行捕获处理的,即使没有使用try…catch语句捕获或使用throws关键字声明抛出,程序也能编译通过,只是在运行过程中可能报错。

(4)运行时异常错误分析

运行时异常一般是由于程序中的逻辑错误引起的,在程序运行时无法恢复。

(5)运行时异常示例

a. 数组下标越界异常

在这里插入图片描述

b. 空指针异常

在这里插入图片描述

c. 算术异常

在这里插入图片描述

d. 类型转换异常

在这里插入图片描述

e. 数字格式异常

在这里插入图片描述

三、异常捕获

当程序发生异常时,会立即终止,无法继续向下执行。为了保证程序能够有效的执行,Java中提供了一种对异常进行处理的方式——异常捕获。

1、异常捕获try…catch语句

(1)语法格式

try {
    // 可能发生异常的语句
} catch(Exception类或其子类 e){
    // 对捕获的异常进行相应处理
}

(2)案例演示

  • 创建Example1806
    在这里插入图片描述
package net.hw.lesson18;

import java.util.Scanner;

/**
 * 功能:演示异常捕获
 * 作者:华卫
 * 日期:2020年05月16日
 */
public class Example1806 {
    /**
     * 整数除法
     *
     * @param m
     * @param n
     * @return q
     */
    public static int divide(int m, int n) {
        int q = m / n;
        return q;
    }

    public static void main(String[] args) {
        int m, n, q;
        Scanner sc = new Scanner(System.in);

        System.out.print("m = ");
        m = sc.nextInt();
        System.out.print("n = ");
        n = sc.nextInt();

        q = divide(m, n);

        System.out.println(m + " ÷ " + n + " = " + q);
    }
}
  • 运行程序,查看结果

如果除数n不为0,程序运行正常,输出除法的结果:
在这里插入图片描述
如果除数n为0,程序抛出异常后终止,无法输出除法的结果:
在这里插入图片描述

  • 修改divide()方法,对可能导致异常的代码进行异常处理
public static int divide(int m, int n) {                        
    try {                                                       
        int q = m / n;                                          
        return q;                                               
    } catch (Exception e) { // 捕获异常进行处理                         
        System.err.println("异常信息:" + e.getMessage());           
    }                                                           
    return Integer.MIN_VALUE; // 程序异常时返回最小整数                    
}                                                               
  • 修改main()方法,对调用方法返回结果进行判断
public static void main(String[] args) {                    
    int m, n, q;                                            
    Scanner sc = new Scanner(System.in);                    
                                                            
    System.out.print("m = ");                               
    m = sc.nextInt();                                       
    System.out.print("n = ");                               
    n = sc.nextInt();                                       
                                                            
    q = divide(m, n);                                       
    if (q == Integer.MIN_VALUE) {                           
        System.err.println("程序发生了异常!");                     
    } else {                                                
        System.out.println(m + " ÷ " + n + " = " + q);      
    }                                                       
}                                                           
  • 运行程序,查看结果
    在这里插入图片描述

(3)简要说明

  • 在定义的整数除法运算方法divide()中对可能发生异常的代码用try…catch语句进行了捕获处理。在try{}代码块中发生除0异常,程序会转而执行catch(){}中的代码,通过调用Exception对象e的getMessage()方法,即可返回异常信息:“/ by zero”。catch(){}代码块对异常处理完毕后,程序仍会向下执行,执行return Integer.MIN_VALUE;,而不会因为异常而终止运行。
  • 需要注意的是,在try{}代码块中,发生异常语句后面的代码是不会被执行的,比如发生异常的语句“int q = m / n;”后面的返回语句“return q;”就没有执行。
  • 在程序中,如果希望有些语句无论程序是否发生异常都要执行,这时就可以在try…catch语句后,加一个finally{}代码块。

2、异常捕获try…catch…finally语句

(1)语法格式

try {
    // 可能发生异常的语句
} catch(Exception类或其子类 e){
    // 对捕获的异常进行相应处理
} finally {
    // 无论是否发生异常,此代码块代码总会执行
}

(2)案例演示

  • 修改Example1806里的divide()方法,添加finally{}代码块
    在这里插入图片描述
  • 运行程序,查看结果
    在这里插入图片描述
    在这里插入图片描述

(3)简要说明

  • 由此可见,无论程序是否发生了异常,finally{}代码块里的语句都会被执行。正是由于这种特殊性,在程序设计时,经常会在try…catch后面使用finally{}代码块来完成善后工作,比如释放系统资源、关闭数据库连接、关闭线程池等。
  • 如果在try…catch中执行了System.exit(0)语句,finally{}中的代码就不会被执行。因为System.exit(0)语句表示退出当前的Java虚拟机,既然Java虚拟机都停止了,当然任何代码都不能再执行了。
    在这里插入图片描述
    由于在catch{}代码块里添加了一条System.exit(0)语句,于是finally{}代码块里的语句就没有被执行,输出信息里就没有“无论程序是否异常,都会执行~”了。

3、捕获多个异常

如果有多个异常要处理,一般是先处理小异常,再依次处理较大的异常。

(1)错误:大异常在小异常之前

在这里插入图片描述

(2)正确:小异常在大异常之前

在这里插入图片描述

四、抛出异常

1、为什么要抛出异常

一般在程序开发中,开发者通常会意识到程序可能出现的问题,可以直接通过try…catch对异常进行捕获处理。但有些时候,方法中代码是否会出现异常,开发者并不明确或者并不急于处理,为此,Java允许将这种异常从当前方法中抛出,然后让后续的调用者在使用时再进行异常处理。

2、使用throws关键字抛出异常

在Java中,将异常抛出需要使用throws关键字来实现,该关键字用在会抛出异常的方法名称后,同时支持一次性抛出多种类型的异常。

(1)语法格式

 [修饰符] 返回值类型 方法名([参数类型 参数名1...]) throws 异常类1, 异常类2, ... {
	// 方法体...
}

throws关键字需要写在方法声明的后面,并在后面需要声明方法中发生异常的类型。

(2)案例演示

  • 创建Example1807
    在这里插入图片描述
package net.hw.lesson18;

import java.util.Scanner;

/**
 * 功能:演示throws关键字
 *      为方法声明抛出异常
 * 作者:华卫
 * 日期:2020年05月16日
 */
public class Example1807 {
    public static int divide(int m, int n) throws Exception {       
        int q = m / n;
        return q;      
    }

    public static void main(String[] args) {
        int m, n, q;
        Scanner sc = new Scanner(System.in);

        System.out.print("m = ");
        m = sc.nextInt();
        System.out.print("n = ");
        n = sc.nextInt();

        q = divide(m, n);
        
        System.out.println(m + " ÷ " + n + " = " + q);
    }
}

但是,程序编译报错:
在这里插入图片描述
将鼠标移到红色波浪线上,按组合键<Alt>+<Enter>,系统给出两种快速解决方案:
在这里插入图片描述

  • 解决方法一:Surround with try/catch 表示在出现异常的代码处使用try…catch代码块进行捕获处理。
    在这里插入图片描述
package net.hw.lesson18;

import java.util.Scanner;

/**
 * 功能:演示throws关键字
 *      为方法声明抛出异常
 * 作者:华卫
 * 日期:2020年05月16日
 */
public class Example1807 {
    public static int divide(int m, int n) throws Exception {
        int q = m / n;
        return q;
    }

    public static void main(String[] args) {
        int m, n, q;
        Scanner sc = new Scanner(System.in);

        System.out.print("m = ");
        m = sc.nextInt();
        System.out.print("n = ");
        n = sc.nextInt();

        try {
            q = divide(m, n);
            System.out.println(m + " ÷ " + n + " = " + q);
        } catch (Exception e) {
            System.err.println("异常信息:" + e.getMessage());
        }
    }
}

运行程序,结果如下:
在这里插入图片描述

  • 解决方法二:Add exception to method signature 表示在方法上继续使用throws关键字抛出异常
    在这里插入图片描述
    此时运行程序,查看结果:
    在这里插入图片描述
    虽然在主方法上添加了throws关键字抛出异常,编译确实能通过,但是程序发生了异常,终究是需要进行处理的,既然没有处理,那么最终就会导致程序终止运行。

(3)简要说明

当调用者在调用有抛出异常的方法时,除了可以在调用程序中直接进行try…catch异常处理外,也可以根据提示使用throws关键字继续将异常抛出,这样程序也能编译通过。但是,程序发生了异常,终究是需要进行处理的,如果没有被处理,程序就会非正常终止。

五、课后作业

任务、给排序静态方法抛出异常

  • 编写排序静态方法:public static void sort(int[] arr) throws Exception {}
  • 主方法:静态初始化数组arr,输出全部数组元素,调用sort(arr),输出全部数组元素
  • 对可能出错的代码进行异常处理(传入的arr参数是null时就要抛出异常)

当传入sort()方法的数组参数arr不是null时,结果如下:
在这里插入图片描述
当传入sort()方法的数组参数arr是null时,结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/howard2005/article/details/106145250
今日推荐