『JavaSE』异常

本篇博客介绍Java中的异常机制及其基本使用。

什么是异常?


首先,我们再来回顾一下刚开始接触Java时犯的一些错误

  • 除0操作
    在这里插入图片描述
    在这里插入图片描述
  • 数组下标越界
    在这里插入图片描述
    在这里插入图片描述
  • 访问null对象
    在这里插入图片描述
    在这里插入图片描述

上述几个出错程序运行之后打出的Exception开头的红色的提示就是异常信息。那什么是异常呢?

  • 异常就是指程序运行时出现错误时通知程序调用者的一种机制
  • 注意异常时运行时的一种机制,不是编译期。我们可以举个例子来理解一下什么是编译期的错误,比如我们写代码时出现关键字的拼写错误,此时出现的错误就是编译期的错误。

防御式编程


什么是防御式编程

  • 防御式编程时提高软件质量技术的有益辅助手段;
  • 防御式编程的主要思想是:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据
  • 这种思想将可能出现的错误造成的影响控制在有限的范围内。

错误在代码中是客观存在的。因此我们要让程序出现问题的时候及时通知程序猿。主要有两种方式:

  • LBYL:Look Before You Leap,在操作之前就做充分的检查
  • EAFP:It’s Easier to Ask Forgiveness than Permission,先操作,遇到问题再处理

异常的核心思想就是EAFP(先操作,遇到问题再处理)

异常的优点


我们通过一个简单的例子来看一下网络通信LBYL和EAFP的处理流程

  • LBLY方式
    在这里插入图片描述
  • EAFP方式
    在这里插入图片描述

对比两种风格的代码,我们可以发现:

  • LBLY处理方式会将正常流程和错误处理流程代码混在一起,代码整体显得比较混乱;
  • EAFP处理方式正常流程和错误处理流程是分开的,更容易理解代码。

异常的基本用法


异常捕获


基本语法如下

try {
	// 可能出现异常的语句;
} [catch (异常类型 异常对象) {
	// 异常的处理
} ...]
[finally {
	// 异常出口
}]
  • try代码块中放的是可能出现异常的代码
  • catch代码块中放的是出现异常后的处理行为
  • finally代码块中的代码用于处理善后工作,会在最后执行
  • 其中catch和finally都可以根据情况选择加或者不加

下面我们看几种常见的异常处理方式

  • 不处理异常
    在这里插入图片描述
    在这里插入图片描述
    可以看到,如果对于异常不进行处理程序就会在出现异常处终止后序的代码将不再执行。其实这里异常并不是没有被处理,而是被JVM处理JVM处理异常的方式就是打印出现异常的调用栈信息并终止程序
  • 使用try catch后的程序执行过程
    在这里插入图片描述
    在这里插入图片描述
    从运行结果可以看出,try中一旦有了异常,就会跳到对应的catch中,不再执行try中剩余的逻辑
  • catch只能处理对应种类的异常
    在这里插入图片描述
    在这里插入图片描述
    可以看到,这里的catch语句并没有捕获到数组访问越界的异常,该异常最终被JVM处理
  • catch可以有多个
    在这里插入图片描述
    在这里插入图片描述
    一段代码可能会抛出多种不同的异常,不同的异常有不同的处理方式。因此可以搭配多个catch代码块如果多个异常的处理方式是完全相同的,也可以写成如下形式
    在这里插入图片描述
    在这里插入图片描述
  • 可以使用一个catch捕获所有的异常Exception类是所有异常类的父类。因此可以用这个类型表示捕获所有异常。catch进行匹配的时候,不仅可以捕捉到相同类型的异常,还可以捕捉到目标类型异常的子类对象。不推荐使用这种方式。
    在这里插入图片描述
    在这里插入图片描述
  • finally表示最后的善后工作,如释放资源
    在这里插入图片描述
    在这里插入图片描述
    无论try中是否发生异常,finally中的代码一定会执行
  • 可以使用try回收资源
    在这里插入图片描述
    和前一个代码的写法等价,将Scanner对象在try的()中创建,能够保证在try执行完毕后自动调用Scanner的close方法
  • 如果当前方法中没有合适的异常处理方式,异常就会沿着调用栈向上传递,直到最后交给JVM处理
    在这里插入图片描述
    在这里插入图片描述

异常处理流程


  • 程序先执行try中的代码
  • 如果try中的代码出现异常,就会结束try中的代码,看和catch中的异常类型是否匹配
  • 如果找到匹配的异常类型,就会执行catch中的代码
  • 如果没有找到匹配的异常类型,就会将异常向上传递到上层调用者;
  • 无论是否找到匹配的异常类型,finally中的代码都会被执行到在该方法结束之前执行);
  • 如果上层调用者也无法处理异常,异常就会继续向上传递
  • 一直到main方法也没有合适的代码处理异常,就会交给JVM来进行处理,此时程序就会异常终止。

异常抛出


除了Java内置的类会抛出一些异常之外,程序猿也可以手动抛出某个异常。使用throw关键字来完成这个操作。
下面来看一个具体的例子
在这里插入图片描述
在这里插入图片描述

异常说明


我们在处理异常时,通常希望知道这段代码中究竟会出现哪些可能的异常。我们可以使用throws关键字,把可能抛出的异常显式的标注在方法定义的位置。从而提醒调用者要注意捕获
在这里插入图片描述

finally的注意事项


我们知道finally中的代码保证一定能够被执行到,而且是在return语句之前,有时会带来一些麻烦,我们看下面一个代码:
在这里插入图片描述
在这里插入图片描述
注意

  • finally执行的时机是在方法返回之前try或者catch中如果有return会在这个return之前执行finally);
  • 但是如果finally中也存在return语句,那么就会执行finally中的return,从而不会执行到try中原有的return
  • 不建议在finally中写return语句,编译器会有警告
    在这里插入图片描述

Java异常体系


在这里插入图片描述

  • 顶层类Throwable派生出两个重要的子类,Error和Exception;
  • 其中Error指的是Java运行时内部错误和资源耗尽错误应用程序不抛出此类异常。这种内部错误一旦出现,除了告知用户并使程序终止之外,没有别的办法,这种情况很少出现;
  • Exception是程序猿使用的异常类的父类
  • 其中Exception有一个子类称为RuntimeException,这里面又派生出很多我们常见的异常类NullPointerException等

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查异常,所有的其他异常称为受查异常
如果一段代码可能抛出受查异常,那么必须显式进行处理
在这里插入图片描述
在这里插入图片描述
从报错信息可以看出,我们必须对受查异常进行处理
这里有两种处理方式

  • 方法一使用try catch包裹起来进来
    在这里插入图片描述
    在这里插入图片描述
  • 方法二:在方法上加上异常说明,相当于将处理动作交给上级调用者
    在这里插入图片描述
    在这里插入图片描述

自定义异常类


Java中虽然已经内置了丰富的异常类,但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展,创建符合我们实际情况的异常
这里,我们模拟一个用户登录的场景:

  • 首先,我们自定义两个异常类UserException和PasswordException
    在这里插入图片描述
  • 下面我们来写程序主逻辑
    在这里插入图片描述
  • 运行效果如下
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

注意

  • 自定义异常通常会继承自Exception或者RuntimeException
  • 继承自Exception的异常默认是受查异常
  • 继承自RuntimeException的异常默认是非受查异常
  • 自定义异常类往往不是创建一个类,而是创建一个系列
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章