Java 深入理解Java异常以及异常处理机制

导读:本篇文章讲解 Java 深入理解Java异常以及异常处理机制,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

异常

1.异常的定义

异常就是有异于常态,和正常情况不一样,有错误出错。在Java中,阻止当前方法或作用域的情况,称之为异常。
发生异常的原因有很多,这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。通常有这几类:

①用户输入了非法数据。
②要打开的文件不存在。
③网络通信时连接中断,或者JVM内存溢出。

异常是程序中的一些错误,但并不是所有的错误都是异常。举两个栗子,相信我们每个人一开始学编程的时候都会犯这样一个错误,老是少写一个分号,那么运行出来结果是错误,提醒你少了一个分号。
在这里插入图片描述

还有不小心用0做了除数,会抛出java.lang.ArithmeticException 的异常。
在这里插入图片描述
再有就是不小心访问数组越界,运行就会提示你ArrayIndexOutOfBoundsException。
在这里插入图片描述
虽然我们每每遇到类似的错误,心里总有那么一丝丝不舒服,但是这些错误都是可以避免的。

2.异常的三种类型

①编译期异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
② 运行时异常: 运行时异常是可能被程序员避免的异常。与编译期异常相反,运行时异常可以在编译时被忽略。
③ 错误: 错误脱离程序员控制的问题,错误在代码中通常被忽略。例如,当我们写了一个递归函数,没有给退出条件,就会产生栈溢出,一个错误就发生了,它们在编译也检查不到的。

2.1编译期异常

打开了一个不存在的文件,这时就会在编译期报错,需要进行异常处理。
在这里插入图片描述

2.2运行期异常

由系统检测, 数组下标越界,编译期间不报错,运行期间报错。
在这里插入图片描述

2.3错误

栈溢出
在这里插入图片描述

3.异常的继承关系

在这里插入图片描述
Java中的所有不正常类都继承于Throwable类。Throwable主要包括两个大类,一个是Error类,另一个是Exception类; 其中Error类中包括虚拟机错误和线程死锁,一旦Error出现了,程序就彻底的挂了,被称为程序终结者;Exception类,也就是通常所说的“异常”,主要指编码、环境、用户操作输入出现问题。Exception主要包括两大类,非检查异常(RuntimeException)和检查异常(其他的一些异常)。RuntimeException异常主要包括以下四种异常(其实还有很多其他异常):空指针异常、数组下标越界异常、类型转换异常、算术异常。RuntimeException异常会由java虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。
在这里插入图片描述

4.Java异常处理机制

异常处理机制关键字

try、catch、finally、throw、throws。
Java中处理异常有两种方式:捕获异常、声明抛出异常。
捕获异常: 就地解决,并使程序继续执行。
声明抛出异常:将异常向外转移,即将异常抛出方法之外,由调用该方法的环境去处理。


try:捕获异常的第一步是用try{…}选定捕获异常的范围,try模块中的语句是程序正常流程要执行的语句,但是在执行过程中有可能出现异常。所有可能抛出异常的语句都放入try模块中。
catch:每个try代码块可以伴随一个或多个catch语句,用于处理try代码块中所生成的异常事件。catch语句只需要一个形式参数指明它所能够捕获的异常类型,这个类必须是Throwable的子类,运行时系统通过参数值把被抛弃的异常对象传递给catch块。在catch块中是对异常对象进行处理的代码,与访问其它对象一样,可以访问一个异常对象的数据成员或调用它的方法。
finally:捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。不论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。

①finally 关键字用来创建在 try 代码块后面执行的代码块。
②无论是否发生异常,finally 代码块中的代码总会被执行。
③在finally 代码块中,可以运行清理类型等收尾善后性质的语句。

throw:在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象。生成的异常对象将传递给Java运行时系统,这一异常的产生和提交过程称为抛出(throw)异常。
throws:如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法名的尾部。 (也可以使用 throw 关键字抛出一个异常。)

5.异常处理示例

5.1异常的捕获

5.1.1 try catch finally

try{ //一些会抛出的异常
}catch(异常类型 异常名(变量)){
//第一个catch //处理该异常的代码块
}catch(异常类型 异常名(变量)){
//第二个catch,可以有多个catch //处理该异常的代码块
}finally{
//最终要执行的代码
}

1)在上述我们提到如果访问数组下标越界,运行就会抛出一片红,提醒你ArrayIndexOutOfBoundsException,当我们看到一片红是不是觉得很难受,要是我们不想看到它报一片红,可以用try catch来处理,来委婉的提醒你数组下标越界了。

public class TestDemo {
    public static void main(String[] args) {
        int[] arr = {1,0,2,4};
        try{
            System.out.println(arr[4]);
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("数组下标越界了");
        }

    }

运行结果:
在这里插入图片描述
JVM会抛出一个异常,然后catch就会捕获异常并处理。
2)catch语句块中异常捕获顺序:
捕获异常的顺序和catch语句的顺序有关,当捕获到一个异常时,剩下的catch语句就不再进行匹配。
在这里插入图片描述
在上述代码中,str是一个空字符串,str.访问操作肯定会出现空指针异常的NullPointerException,可是上述运行结果中只打印了一个“数组下标越界了”,这是因为当捕获到一个异常时,剩下的catch语句就不再进行匹配,也就是所在try中当前代码发生异常,后续有效代码是不会被执行的。
3)有的时候我们会有很多异常,一个catch对应一个异常,就要用好多catch,但是我们只关心一个异常,用多个catch的话就会产生代码的累赘,代码的可读性也会变差,可以异常不处理也不行,编译就会报错。如下,比如我们只关心数组下标越界异常。
在这里插入图片描述
因为所有的异常都是Exception的子类,所以这个时候就可以考虑多态,向上造型,基类的引用 引用 派生类的对象。因此,在安排catch语句的顺序时,首先应该捕获最特殊的异常,然后再逐渐一般化。在这里插入图片描述
注意:一般先安排子类,再安排父类。
在这里插入图片描述
在上述示例中,执行try代码块中,JVM先抛出数组下标越界异常,然后就往下去匹配catch来捕获异常,当匹配到第一个catch(Exception e)满足向上造型,所以第二个catch(ArrayIndexOutOfBoundsException e)就不可能捕获到异常了。因此,当我们用catch的时候一定要先子类后父类
4)finally(可选)
finally块必定会被执行
在这里插入图片描述
finally可在try和单个或多个catch后使用,也可单独和try使用
在这里插入图片描述

5.1.2 注意事项

① catch 不能独立于 try 存在。
②在 try/catch 后面添加 finally 块并非强制性要求的。
③try 代码后不能既没 catch 块也没 finally 块。
④try, catch, finally 块之间不能添加任何代码

5.2抛异常

5.2.1throw

比如我们写一个函数,找到一个整型数组的最后一个元素值并返回,函数返回值类型设为int型,考虑到如果数组的长度为0,那么该return什么呢?返回-1?答案肯定是不行的,要是数组的最后一个元素值是-1呢?那么我们可以在这里用throw,如果数组长度为0,就扔一个异常对象。
所以throw一般用于函数中不知道怎么返回了,可以扔一个异常对象。如下:
在这里插入图片描述
在这里MyException()是自定义的异常类型对象。

5.2.2throws

1)在一个方法中出现异常,我们不想处理该异常,可以选择用throws的方法声明抛出异常。
语法规则:
函数名()throws 异常1,异常2… {
//函数体
}

示例:
在这里插入图片描述
2)一个方法不处理它产生的异常,那么调用它的方法要处理这些异常,否则也要抛出异常,直到最后被主函数调用,主函数调用也是要处理异常的,否则也要抛出异常交给JVM处理。就像是踢皮球一样,你要调用我这个方法就必须要接我这个皮球,接到皮球以后,要不继续踢出去,要不就进行处理。
①一直抛异常
在这里插入图片描述
②有一个方法进行处理,例如下面示例中fun2()对异常进行了处理,那么主函数调用fun2()的时候就不会产生异常。
在这里插入图片描述
总结:

①一个方法不处理它产生的异常,而是沿着调用层次向上传递,由调用它的方法来处理这些异常,叫声明抛出异常(throws)。声明抛出异常是一种消极的异常处理机制。
②抛出异常就是产生异常对象的过程,首先要生成异常对象,异常或者由虚拟机生成,或者由某些类的实例生成,也可以在程序中生成。在方法中,抛出异常对象是通过throw语句实现的。
③可以抛出的异常必须是Throwable或其子类的实例。
④Throwable或Exception类,建议用Exception类。用户定义的异常同样要用try–catch捕获,但必须由用户自己抛出 thrownew MyException()。

6.用户自定义异常

在 Java 中可以自定义异常。
自定义异常规则:

①所有异常都必须是 Throwable 的子类。
②如果希望写一个编译期异常类,则需要继承 Exception 类。
③如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

如下:
class MyException extends Exception{

}
只继承Exception 类来创建的异常类是编译期异常类。 一个异常类和其它任何类一样,包含有变量和方法。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/95562.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!