详解Java中的异常机制:运行期异常、编译器异常及如何自定义异常

本文详细介绍了Java中的异常处理机制,包括运行期异常、编译期异常的捕获和处理,finally关键字的应用,自定义异常的创建,以及异常处理的注意事项。通过实例展示了try...catch的使用方式,强调了编译器异常与运行期异常的区别,并提供了自定义异常的代码示例。
摘要由CSDN通过智能技术生成


前言

首先举一个现实生活中的例子,带大家理解异常。
小李是一个骑行爱好者,有一天他骑着自行车去西藏旅行。
一种情况:骑着骑着车轱辘坏了,是个大的问题,该问题他没有能力解决不了。(error
另一种情况:车胎漏气了,是个小问题,该问题他有能力解决,但可以不解决。(运行期exception
另一种情况:出门前,他检查出手刹松了,该问题他必须解决,才能上路。(编译器exception

一、异常概述及分类

1.异常概述

  • 异常就是Java程序运行过程中出现的错误。
  • Java中有一个类Throwable类用于描述,错误和异常。

2.异常的继承结构

  • Error错误
    严重性无法解决的问题。(车轱辘飞了)
  • Exception异常
    一般性可解决的问题。
    - 编译器异常:发生在编译器期间,必须要处理。(手刹松了)
    - 运行期异常:发生在运行期间,选择处理或不处理。(车胎漏气)

3.异常的继承机构图

在这里插入图片描述

二、运行期异常-RuntimeException

1.JVM如何默认处理异常

首先我们演示一个运行期异常。
代码:

public class 运行期异常 {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        System.out.println(a / b);
        System.out.println("继续运行");
    }
}

执行结果:
在这里插入图片描述

  • 我们没有处理运行期异常,默认交由JVM处理,JVM处理异常的方法是:打印异常的信息,然后退出JVM,不执行接下来的操作。

2.try…catch的方式处理单个异常

我们对JVM默认处理异常的方式不满意,希望异常不影响接下来代码的运行,我们可以适用**try…catch**捕获异常,不交由JVM处理。

  • 格式
try	{
			可能出现问题的代码 ;
		}catch(异常类型 变量名){
			针对问题的处理 ;
		}
  • 代码示例
public class 运行期异常 {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        //试图捕获可能出现的异常
        //try里面的代码:有可能出现异常的代码,不一定必须存在异常
        //catch(异常类型 异常变量名) :一旦try里面发生了catch中所捕获的异常类型,catch里面的代码就会执行。所以catch里面就放我们处理具体处理异常的操作。
        try {
            System.out.println(a / b);
        }catch (ArithmeticException e){
            System.out.println("初始为0");

        }

        System.out.println("继续运行");
   
    }
}
  • 注意事项
    • try中的代码越少越好
    • catch中要做处理,哪怕是输出一条语句,不要将异常信息隐藏

3.try…catch的方式处理多个异常

当程序中出现多个异常时,我们可以使用多个catch进行异常的捕获。

  • 多个异常时并列关系,放置顺序无所谓,如果异常类有父子关系,父类要放后面
  • 对于始料不及的异常,我们可以最后使用所有异常的父类Exception来捕获。(但是我们最好能够明确代码中可能出现的异常,不建议使用该方法)

代码示例:

public class 运行期异常2 {
    public static void main(String[] args) {
        int [] arr={1,2,3};
        //存在角标越界异常,是运行期异常
        try {
            arr[10] = 20;
            int a=10/0;
             arr = null;
            System.out.println(arr.length);
        //注意:如果捕获的异常不是出现的异常的情况,仍然是交由JVM处理
        }catch (ArrayIndexOutOfBoundsException a){
        //调用该方法可以打印详细的异常信息
            a.printStackTrace();
        }catch (ArithmeticException e){
            System.out.println("除数为0的异常");
        }catch (Exception c){
            System.out.println("其他异常");
        }
        System.out.println("继续执行");

    }
}
//!!!注意代码的执行顺序:捕获到try中的第一个异常,并使对应的catch进行捕获处理后,下一步不再执行try内的代码,而是执行程序接下来的代码。

4.代码示例

需求:用户输入一个整数,如果不是整数需要重新输入

  • 调用scanner方法:调用hasNextInt()方法进行判断
public class 运行期异常的举例 {
    public static void main(String[] args) {
        while(true){
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数");
            //调用hasNextInt()方法实现
            if(scanner.hasNextInt()){
                int i = scanner.nextInt();
                System.out.println(i);
                System.out.println("输入正确");
                break;
            }else {
                System.out.println("请重新输入");
            }
        }


    }
}
  • 使用try…catch实现
public class 运行期异常的举例 {
    public static void main(String[] args) {
        while(true){
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数");
        try {
            int i = scanner.nextInt();
            System.out.println(i);
            break;
        //通过该代码我们更好的了解catch如何使用,但是我们通常是输出异常信息
        } catch (InputMismatchException e) {
             System.out.println("输入有误,请重新输入");
            }
        }

    }
}

三、编译期异常-非RuntimeException及其子类

1.编译器异常的处理方式一(

  • 使用try…catch自己捕获处理

代码示例:

public class 编译器异常处理方式1 {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //编译器异常必须处理,处理方式有两种
            //1.使用try...catch自己捕获处理
        try {
            Date date=simpleDateFormat.parse("2020202-1");
        }catch (ParseException p){
            p.printStackTrace();
        }
        System.out.println("继续执行");

    }
}

执行效果:
在这里插入图片描述

2.编译器异常的处理方式二(

  • 采用throws抛异常,甩锅给调用者,谁调用谁处理。

代码示例1:main方法抛异常,交由JVM处理异常

public class 编译器异常处理方式2 {
    //2.采用throws抛异常,甩锅给调用者,谁调用谁处理。
    //一般甩给main方法就不再甩了,如果继续甩到虚拟机接下来的代码就无法执行了。所以一般在main方法中捕获异常。
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse("202101-23");
        System.out.println("继续执行");
    }

}

代码示例2:main方法抓异常

public class 编译器异常处理方式2 {
    public static void main(String[] args) {
        //main方法调用了,即将存在编译器异常的代码甩给了main方法
        //此时main方法可选择抛异常或者抓异常
        //抛异常:交给jvm处理,不再执行接下来的代码
        //抓异常:执行接下来的代码(一般需求需要抓)
        try {
            show();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("继续执行");

    }
    public  static void show() throws ParseException {
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
        Date d=s.parse("202020-2");


    }
}

四、finally关键字和throw关键字

1.finally关键字

  • 用在try…catch…语句中来释放资源,其特点是始终被执行。
  • 代码示例:
public class 关键字finally {
    public static void main(String[] args) {
        try {
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("catch代码执行了");
            //catch里面是try里面发生了所捕获的异常,那么catch里面的代码才会执行

        }finally {
            //不管try里面有没有遇到异常,finally里面的代码都会执行
            //常用于善后处理工作
            System.out.println("finally内的代码执行了");
        }
    }
}

2.throw关键字

  • 在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
  • 代码示例:
public class 关键字throw {
    //throws 对编译器异常,进行抛出,抛给调用者去处理
    //throw 用在方法内部,对异常进行抛出
    public static void main(String[] args) {
        double r = division(12, 0);
        System.out.println("继续执行");
        System.out.println(r);
    }


    private static double division(int a, int b) {
       double r=0;
       //一旦判断除数为0
        if(b==0){
            //使用throw抛出异常,后续代码不再执行
            throw new ArithmeticException("除数为0");
        }else{
            r=a/b;
        }
        return r;
    }
}
  • 注意
    使用throw抛出异常,代码将不再执行。

2.throws与throw的区别

  • throws
    • 用在方法声明后面,跟的**异常类名**
    • 可以跟多个异常类名,用逗号隔开
    • 表示抛出异常,由该方法的调用者来处理
    • throws表示出现异常的一种可能性,并不一定会发生这些异常
  • throw
    • 用在方法体内,跟的是**异常对象名**
    • 只能抛出一个异常对象名
    • 这个异常对象可以是编译期异常对象,可以是运行期异常对象
    • 表示抛出异常,由方法体内的语句处理
    • throw则是抛出了异常,执行throw则一定抛出了某种异常

五、自定义异常

  • 因为在以后的开发过程中,我们可能会遇到各种问题,而Jdk不可能针对每一种问题都给出具体的异常类与之对应, 为了满足需求,我们就需要自定义异常。

代码示例1:银行取款余额不足异常

public class 自定义异常 {
    static int money = 100;
    public static void main(String[] args) {

        withdrawal();
    }
//取款的方法
    private static void withdrawal() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的取款金额");
        int num = scanner.nextInt();
        //取款金额小于账号余额
        if(num<=money){
            money-=num;
            System.out.println("取款成功");
        }else{
            //自定义异常抛出
            throw new NoMoneyException("余额不足异常");

        }

    }
}
//自定义余额不足的异常,继承java中的异常类
class NoMoneyException extends  RuntimeException{
    public NoMoneyException() {
        super();
    }

    public NoMoneyException(String message) {
        super(message);
    }
}

代码示例2:学生成绩输出异常

public class 自定义异常之成绩非法异常 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要录入的成绩");
        int i = scanner.nextInt();
        if(i>=0&&i<=100){
            System.out.println("成绩录入成功");
        }else{
            throw new ScoreException("成绩有误");
        }


    }
}
//自定义异常
class ScoreException extends RuntimeException{
    public ScoreException() {
        super();
    }

    public ScoreException(String message) {
        super(message);
    }
}

所谓自定义异常也就是创建我们自定义的异常类继承Java中的异常类,并在合理的处抛出异常满足程序需求即可。

六、异常的注意事项(针对编译器异常)

  1. 子类在重写父类方法时,父类方法没有抛出异常,子类就不能抛出异常
  2. 如果父类抛出异常,子类在重写该方法时,可以抛出与父类方法一样的异常或者该异常的子类,也可以不抛异常,自己捕获。
  3. 子类抛出的异常,不能比父类大只能是父类异常或该异常的子类

总结

通过本文我们详细的了解了Java中的异常机制,其实在我们实际开发中遇到运行期异常,只需要选中我们认为可能会出现问题的代码,按住快捷键**ctrl+alt+t** 捕获处理即可。对于编译器异常,按住快捷键**alt+enter**,根据需求选择抛异常或者抓异常即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值