Java异常处理机制

1. 异常(Exception)

1.1 概述

异常是指程序运行时可能由于外部系统的变更导致程序出错的情况,我们在以往的学习中碰到过数组越界、空指针、除数为0等在编译时idea没有出现红色波浪线但运行时出现的错误都叫异常。在发生未处理的异常时程序就会自动停止。面对异常情况时我们曾经想方设法规避,但异常往往防不胜防,于是,Java内置了一套完善的异常处理机制,并对这些异常做了分类。

1.2 异常类型

 Throwable是所有错误(Error)和异常的基类,对于错误的话我们是无法直接处理的,但对于出现的异常我们可以使用一些代码来更好地处理异常,异常的祖先类是Exception,当出现了异常不知道是什么类型,我们就捕获Exception类的异常即可。

IOException输入/输出流异常,经常出现在文件读写上,其派生类分别为文件结束异常(EOFException)、找不到文件异常(FileNotFoundException)、URL异常(MalformedURLException)和UnknownHostException,这里面后两个异常虽然本Reno叫不准名字,但推断出来应该是和Web有关的异常

RuntimeException运行时异常,这个是在运行时出现的异常,并且是以往学习中出现次数最多的异常,其派生类包括(仅展示本Reno学过的异常)

1. ClassCastException强制类型转换异常,继承同一级别的不同派生类之间不能强制类型转换(比如同样继承Animal类,Cat与Dog就不能互相转换),强制类型转换仅支持向下转型(对于基本数据类型就是将范围较大的数据类型强制转换为范围较小的数据类型,对于类类型就是将基类指针强制转换为派生类),对于不支持的强制类型转换必然抛出此类异常

2. ArithmeticException算术异常,通常在整数相除时除数为0会出现

3. IllegalArgumentException非法参数异常,里面包含了NumberFormatException异常,在把数字的字符串形式转换为基本数据类型过程中,如果字符串带有除数字和小数点以外的其余字符必然抛出此类异常。

4. IndexOutOfException索引越界异常,包括IndexOutOfException数组下标越界异常

5. NullPointer空指针异常,当类类型的指针指向null是不可以访问这个类的属性的,否则就会抛出此类异常。 

直接继承Exception的异常类的还有ClassNotFoundException和CloneNotSupportException,由于本Reno也是第一次看见就不细讲了。

除此之外还可以自定义一个异常,但需要继承Exception类同时定义有参和无参构造函数,其中有参构造函数的参数是一个描述异常具体信息的字符串。同时异常可以分为Checked异常和UnChecked异常,Checked异常是在编译时就检测的错误(不是编译错误),对于可能出现异常的函数应当声明throws哪些异常;UnChecked异常编译器不会检测,运行时才会检测。

Tips:所有异常对象在被捕获时都可以调用该对象的printStackTrace()来更精准地显示出现的异常类型和位置

2. 异常处理办法

2.1 抛出(throw)异常 

在调用一个函数的过程中当异常出现但不知如何处理时,会抛出一个异常交给上级调用者,异常抛出后会按照调用层级一级一级往上回溯直到有捕获相应异常的调用者,或者回溯到主函数为止。在这要注意的时无论是哪个函数抛出的异常必须有其上级调用者捕获并处理才能保证程序正常运行,否则程序会中断。

throw关键字

代码格式

throw new 异常类型(异常信息);

执行这个语句时会立即抛出相应的异常,抛出的是一个异常对象,调用者捕捉到这个异常对象后才能处理异常,throw通常和if语句搭配,同时也有与throw相似但易混淆的throws关键字。

throws关键字

代码格式

throws [可能出现的异常类型,如果有多个用逗号隔开]

如throws RuntimeException

throws是用来声明函数可能出现的所有异常,这串代码要写在函数的参数列表后面,函数体前面(即右小括号后面,左大括号前面)。

2.2 捕获(catch)异常

捕获和处理异常通常会用try--catch(--finally)代码块。

代码格式:

try {

        //操作1

        //操作2

} catch (异常类型 异常对象名) {

        //操作3

} finally {

        //操作4

}

操作1:可能出现异常的操作语句,若出现异常则不会执行到语句2,操作1的尝试执行的操作也会回退,并执行操作3

操作2:只有未出现异常才会执行到的语句

操作3:出现异常后负责处理异常或给出提示的语句

操作4:无论是否产生异常,都会执行到的语句

try代码块表示要尝试执行的一系列操作,以及未出现异常将执行的一些操作,当出现异常时try代码块的所有操作全部回退,会从catch代码块的起始位置执行。

catch代码块表示出现异常时执行相应的操作去处理异常。

注意:catch后面要捕捉的异常类型必须是Exception的派生类。

如果有多个catch代码块应当先捕捉派生类异常,再捕捉基类异常,这样可以更加精准判断异常类型,如果先捕获基类异常,就没必要再去捕获派生类异常了。

finally代码块无论是否出现异常都会执行,finally代码块可有可无,按实际需求决定是否写finally。

小练习

写一个方法void isTriangle(int a,int b,int c),判断三个参数是否能构成一个三角形, 如果不能则抛出异常IllegalArgumentException,显示异常信息 “a,b,c不能构成三角形”,如果可以构成则显示三角形三个边长,在主方法中得到命令行输入的三个整数, 调用此方法,并捕获异常。

分析:

定义isTriangle()函数时先声明可能发生的异常,函数体内通过判定三个参数是否满足构成三角形的条件,并给出提示信息,如果不满足条件就立即抛出异常。

在主函数中定义三个整数和一个Scanner对象,通过键盘初始化这三个整数,作为调用isTrangle()的参数,调用时注意捕获IllegalArgumentException异常。

import java.util.Scanner;

public class Triangle {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        int b = sc.nextInt();
        int c = sc.nextInt();
      
        try {
            isTriangle(a, b, c);
        } catch (IllegalArgumentException e) {
            System.out.println("八格牙路");
            e.printStackTrace();
        } 
    }

    public static void isTriangle(int a, int b, int c) throws IllegalArgumentException{
        if (a > 0 && b > 0 && c > 0 && (a + c > b) && (a + b > c) && (b + c > a)) {
            System.out.println("可以构成三角形,边长分别为" + a + ", " + b + ", " + c);
        } else throw new IllegalArgumentException("错误:不能构成三角形");
    }
}

总结

1. 程序不能通过编译称为编译错误,程序运行时出现的非严重错误则称为异常,虽然异常看起来防不胜防,但Java的异常处理机制仍能保证程序的正常运行。

2. 处理Java程序的异常需要先由被调用的函数抛出异常,再一级一级向上回溯调用者看看有没有捕捉对应类型异常的代码块,调用者层级最高的是主函数,所以声明主函数时不应该再抛出异常,而是设法捕获被调函数的异常。

3. try代码块可以先尝试执行块里面的操作,出异常时try代码块中异常位置后面的语句不执行,出现异常的操作也会回滚,然后找一下后面的catch代码块中有没有可以捕获类型与所抛出的相匹配的异常。有的话执行对应的catch代码块。

4. 当可能出现的异常类型之间有继承关系,先写捕捉派生类异常的catch,再写捕捉基类异常的catch,便于更加准确界定异常的类型。此外,异常对象名.printStackTrace()也可以方便查看准确的异常类型和出异常的位置。

5. 自己写抛出异常的条件以及自己定义异常都是对Java异常处理机制的灵活运用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值