十一、异常处理

一、异常引入

1.1 两数相除

public class Demo1 {
    public static void main(String[] args) {
        int i = 10;
        Scanner sc = new Scanner(System.in);
        int i1 = sc.nextInt();  // 当录入一个非int类型时:java.util.InputMismatchException
        System.out.println(i / i1);
        // System.out.println(i / 0);  // java.lang.ArithmeticException: / by zero
    }
}
  • 可能会出现的异常

    • 当录入的数据应为int类型,但是录入非int类型数据的时候,出现异常InputMismatchException

    • 除数为0的时候,出现异常:ArithmeticException

  • 异常Exception:在程序的运行过程中,发生了不正常的现象,阻止了程序的运行,我们称之为异常

1.2 处理方式一:if-else

  • 使用hasNextInt判断录入的是否为整数,不是则打印提示

  • 加上if判断除数是否为0,若除数为0,给出提示

  • 代码演示

  • public class Demo1 {
        public static void main(String[] args) {
            int i = 10;
            Scanner sc = new Scanner(System.in);
            int i1 = sc.nextInt();  // 当录入一个非int类型时:java.util.InputMismatchException
            System.out.println(i / i1);
            System.out.println(i / 0);  // java.lang.ArithmeticException: / by zero
        }
    }
    
  • 缺点:

    • 代码臃肿,业务代码和处理异常的代码混合在一起
    • 可读性差
    • 程序员需要花费大量精力来维护这个漏洞
    • 程序员很难解决所有的异常

1.3 处理方式二:try…catch…finally

  • java提供的异常处理机制try...catch...finally
  • 代码演示
public class Demo1 {
    public static void main(String[] args) {
        int i = 10;
        Scanner sc = new Scanner(System.in);
        try {
            int i1 = sc.nextInt();
            System.out.println(i / i1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

二、 异常概述

2.1 异常的体系结构

  • 异常的概述

    • 就是程序出现了不正常的情况。程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
  • 异常的体系结构

  • Error:严重问题,通过代码无法处理。比如内存溢出
  • Exception:称为异常类,表示程序本身可以处理的问题
    • RuntimeException及其子类:运行时异常。例如空指针异常,数组索引越界异常
    • 除RuntimeException之外所有的异常:编译期必须处理的,否则程序不能通过编译。例如日期格式化异常

2.2 编译时异常和运行时异常的区别

  • 编译时异常

    • 都是Exception类及其子类
    • 必须显示处理,否则程序就会发生错误,无法通过编译
  • 运行时异常

    • 都是RuntimeException类及其子类
    • 无需显示处理,也可以和编译时异常一样处理
  • 图示

在这里插入图片描述

2.3 JVM处理异常的方式

  • 异常处理流程
    • 首先会看,程序中有没有自己处理异常的代码
    • 如果没有,交给本方法的调用者处理(向上传递)
    • 如果调用者也没有处理,最终这个异常会交给JVM虚拟机默认处理
  • JVM 默认处理的两个步骤:
    • 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
    • 程序停止执行

2.4 查看异常信息

  • 控制台在打印异常信息:
    • 打印异常类名
    • 异常出现的原因
    • 异常出现的位置
  • 我们调bug时,可以根据提示,找到异常出现的位置、分析原因、修改异常代码

在这里插入图片描述

三、几种异常的处理方式

3.1 throws方式处理异常

  • 定义格式

    public void 方法() throws 异常类名 {
        
    }
    
  • 注意事项

    • 这个throws格式是跟在方法的括号后面的
    • 编译时异常必须要进行处理,两种处理方案:try...catch …finally或者 throws,如果采用 throws 这种方案,在方法上进行显示声明,将来谁调用这个方法谁处理
    • 运行时异常因为在运行时才会发生,所以在方法后面可以不写,运行时出现异常默认交给jvm处理

在这里插入图片描述

  • 运行时异常报错

在这里插入图片描述

3.2 throw抛出异常

  • 格式:throw new 异常();

  • 注意

    这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码不用再执行了

  • throwsthrow的区别

    throwsthrow
    用在方法声明后面,跟的是异常类名用在方法体内,跟的是异常对象名
    表示声明异常,调用该方法有可能会出现这样的异常表示手动抛出异常对象,由方法体内的语句处理
  • 还是要调用者处理,不常单独用

3.3 try-catch方式处理异常

  • 定义格式

    try {
    	// 可能出现异常的代码;
    } catch(异常类名 变量名) {
    	// 异常的处理代码;
    }
    
  • 执行流程

    • 把可能出现异常的代码放入try代码块中,
    • 若出现异常,底层会将异常封装为对象,被catch后面()中的那个异常对象接收,执行对应catch代码块里的内容;(try中出现异常那行后续的代码不执行)
    • 若无异常或异常不匹配,catch无法捕获,则catch代码块内容不执行
    • 无论是否出现异常,finally中的代码块都一定会执行
    • 异常处理结束后,后面的逻辑代码正常执行
  • 示例代码

public class Demo03 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);  // 发现异常,直接执行catch块内语句
            System.out.println("这里访问不到");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
        }
    }
}
  • 一些问题
  1. 如果 try 中没有遇到问题,怎么执行?

    • 会把try中所有的代码全部执行完毕,不会执行catch里面的代码
  2. 如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?

    • 那么直接跳转到对应的catch语句中,try下面的代码就不会再执行了
      当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码
  3. 如果出现的问题没有被捕获,那么程序如何运行?

    • 那么try...catch就相当于没有写.那么也就是自己没有处理。
      默认交给虚拟机处理。
  4. 同时有可能出现多个异常怎么处理?

    • 出现多个异常,那么就写多个catch就可以了.
      注:如果多个异常之间存在子父类关系.那么父类一定要写在下面

3.4 多重catch

  • try中出现异常以后,将异常的类型跟catch后面的类型依次比较,执行第一个与异常类型匹配的catch语句
  • 一旦执行其中一条catch语句以后,后面的catch语句就会被忽略了。所以一般将Exception异常放在最后,用于捕获未知的异常
  • 用于对不同的异常分别捕获,使用不同的处理方法。
  • JDK1.7以后,异常的新处理方式:可以并列用|符号连接
  • 代码示例
public class Demo4 {
    public static void main(String[] args) {
        int i = 10;
        Scanner sc = new Scanner(System.in);
        try {
            int i1 = sc.nextInt();
            System.out.println(i / i1);
        }catch (InputMismatchException e){  // 多重catch
            e.printStackTrace();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }catch (Exception e){  // 父类写在后面
            e.printStackTrace();
        }
    }
}

3.5 Throwable成员方法

  • 在捕获异常catch后的几种常用处理方法:
方法名说明
public String getMessage()返回此 throwable 的详细消息字符串
public String toString()返回此可抛出的简短描述
public void printStackTrace()把异常的错误信息输出在控制台
  • 示例代码
public class Demo05 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);  // 发现异常,直接执行catch块内语句
            System.out.println("这里访问不到");
        } catch (ArrayIndexOutOfBoundsException e) {
            String message = e.getMessage();
            System.out.println(message);
            String s = e.toString();
            System.out.println(s);
            e.printStackTrace();  // 不中断程序的运行
        }
    }

}

在这里插入图片描述

3.6 finally的使用

3.6.1 finally介绍
  • 引入

    • 异常未被捕获、异常被主动throw抛出或try中遇到return都会导致try...catch后面的代码不执行
  • 如何一定执行后面的代码?

    • 将后面的代码用finally{}代码块包裹起来,则一定会执行
    • 只有System.exit(0)会使finally中的代码不执行
  • 示例代码

public class Demo6 {
    public static void main(String[] args) {
        try {
            System.out.println("try...");
            // System.exit(0);  强制退出虚拟机,finally...不打印
            return;
        }catch (Exception e){

        }finally {
            System.out.println("finally...");
        }
        System.out.println("后续代码");  // 代码不运行
    }
}
  • returnfinally的执行顺序(☆)
    • 先执行finally后直接return
3.6.2 什么样的代码会放在finally中呢?
  • 关闭数据库资源
  • 关闭IO流资源
  • 关闭socket资源
  • 等需要系统调用的资源

3.7 异常的练习 (应用)

  • 需求

    键盘录入学生的姓名和年龄,其中年龄为18 - 25岁,超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止

  • 实现步骤

    1. 创建学生对象
    2. 键盘录入姓名和年龄,并赋值给学生对象
    3. 如果是非法数据就再次录入
  • 代码实现

    学生类

public class Student {
    private int age;
    private String name;

    public Student() {
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 18 && age <= 25) {
            this.age = age;
        } else {
            throw new RuntimeException("年龄不合法");
        }

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }


}

  • 测试类
import java.util.InputMismatchException;
import java.util.Scanner;

public class TestStudent {
    public static void main(String[] args) {
        Student student = new Student();
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学生的姓名");
        String name = sc.next();
        student.setName(name);

        while (true) {
            System.out.println("请输入学生年龄");
            int age;
            try {
                age = sc.nextInt();
            } catch (InputMismatchException e) {
                System.out.println(e.toString());
                System.out.println("请输入一个整数");
                sc.nextLine();  // 缓存区还存在非int值,先接受掉
                continue;
            }
            try {
                student.setAge(age);
                break;
            } catch (RuntimeException e) {
                System.out.println(e.toString());
                System.out.println("请重新输入");
                continue;
            }
        }
        String s = student.toString();
        System.out.println(s);
    }
}

  • 运行结果

在这里插入图片描述

3.8 自定义异常

  • 自定义异常概述

    当Java中提供的异常不能满足我们的需求时,我们可以自定义异常

  • 实现步骤(继承后提供两个构造方法)

    1. 定义异常类
    2. 写继承关系
    3. 提供空参构造
    4. 提供带参构造
  • 代码实现

    异常类

public class AgeOutOfBoundsException extends RuntimeException {
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}
  • 学生类
...
        public void setAge(int age) {
        if(age >= 18 && age <= 25){
            this.age = age;
        }else{
            //如果Java中提供的异常不能满足我们的需求,我们可以使用自定义的异常
            throw new AgeOutOfBoundsException("年龄超出了范围");
        }
    }
...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值