java的异常处理机制

1. 异常简介

  程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?。

Java提供了更加优秀的解决办法:异常处理机制。

  异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。

  Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。

  Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。JDK中内建了一些常用的异常类,我们也可以自定义异常。

2. 异常作用

避免因局部异常导致程序崩溃,也可以通过不同异常进行处理。

3. java异常的分类和类结构图

java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。

Throwable又派生出Error类和Exception类。

  错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。

  异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

在这里插入图片描述

总体上我们根据Javac对异常的处理要求,将异常类分为2类。
 1. 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。(语法正常)
 2. 检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。(语法异常)
 3. 需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。

4. 常见运行时异常

NullPointerException空指针异常
 1. name这个对象现在是不存在的,因此调用里面的方法会出现空指针异常。

在这里插入图片描述

ArithmeticException算术异常
 1. 当从键盘输入一个0时就会出现算术异常。

在这里插入图片描述

ClassCastException类型转换异常

在这里插入图片描述

ArrayIndexOutOfBoundsException数组下标越界异常

在这里插入图片描述

5. 异常处理

使用try、catch、finally捕获异常

try{
∥可能会出现异常的代码
}catch(ParseException e){
//捕获执行的代码
}finally{
//不管是否发生异常都要执行的代码
}
e表示本次异常的对象。

在这里插入图片描述

在这里插入图片描述

 1. 当加上return后finally都会被执行

在这里插入图片描述

 2. e.输出异常的详细信息

在这里插入图片描述

6. finally块

  finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。

良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。

需要注意的地方:
 1. finally块没有处理异常的能力。处理异常的只能是catch块。
 2. 在同一try…catch…finally块中 ,如果try中抛出异常,且有匹配的catch块,则先执行catch块,再执行finally块。如果没有catch块匹配,则先执行finally,然后去外面的调用者中寻找合适的catch块。
 3. 在同一try…catch…finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,然后去外围调用者中寻找合适的catch块。
 4. 这是正常的情况,但是也有特例。关于finally有很多恶心,偏、怪、难的问题,我在本文最后统一介绍了,电梯速达->:finally块和return

7. finally块和return

  首先一个不容易理解的事实:在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行

8. throw 异常抛出语句

程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。

  throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。

  throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,其语法格式如下:throw ExceptionObject;

  其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。例如,以下语句在编译时将会产生语法错误:
throw new String(“拋出异常”); //因为String类不是Throwable类的子类

  当 throw 语句执行时,它后面的语句将不执行,此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的异常处理程序。如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。这样逐层向上,直到最外层的异常处理程序终止程序并打印出调用栈情况。

  在某仓库管理系统中,要求管理员的用户名需要由 8 位以上的字母或者数字组成,不能含有其他的字符。当长度在 8 位以下时拋出异常,并显示异常信息;当字符含有非字母或者数字时,同样拋出异常,显示异常信息。代码如下:

在这里插入图片描述

在这里插入图片描述

  如上述代码,在 validateUserName() 方法中两处拋出了 IllegalArgumentException 异常,即当用户名字符含有非字母或者数字以及长度不够 8 位时。在 main() 方法中,调用了 validateUserName() 方法,并使用 catch 语句捕获该方法可能拋出的异常。

运行程序,当用户输入的用户名包含非字母或者数字的字符时,程序输出异常信息,如下所示。

在这里插入图片描述

当用户输入的用户名长度不够 8 位时,程序同样会输出异常信息,如下所示。

在这里插入图片描述

我的测试如下

在这里插入图片描述

9. throws语句

throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常)
 1. 语法:(修饰符)(方法名)([参数列表])[throws(异常类)]{…}
 2. 如: public void function() throws Exception{…}

  当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。如:出现解析异常,将解析异常抛出去

在这里插入图片描述

在这里插入图片描述

  将异常抛出去,自己不处理,但是最后抛来抛去还得进行处理,即使抛给主方法不用处理,但是推荐到主方法就不要抛了,要对其进行处理。

转抛异常在将一个类中的方法异常抛出去,在另一个类中进行处理非常方便。

10. 自定义异常(搞定异常)

自定义异常

在这里插入图片描述

在人 类中使用自定义异常,并将异常抛出去

在这里插入图片描述

在主方法里对人品异常处理

在这里插入图片描述

11. 需要注意的地方

  try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。

  每一个catch块用于处理一个异常。异常匹配是按照catch块的顺序从上往下寻找的,只有第一个匹配的catch会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。

  java中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理catch代码块去执行,异常被处理完后,执行流会接着在“处理了这个异常的catch代码块”后面接着执行。

  有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫做:resumption model of exception handling(恢复式异常处理模式 )

  而Java则是让执行流恢复到处理了异常的catch块后接着执行,这种策略叫做:termination model of exception handling(终结式异常处理模式)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sgy_yuebin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值