Java基础语法—— 认识异常

Posted rain67

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础语法—— 认识异常相关的知识,希望对你有一定的参考价值。


Java基础语法(十)—— 认识异常


接上篇博客 Java基础语法(九)——String 类


本次内容介绍大纲


在这里插入图片描述

一、初识异常


  经常有同学看到异常来问了,异常到底是什么? 而在我们之前的学习中,我们其实已经接触到了Java当中的异常。


1.算数异常


  首先我们遇到的第一个异常是我们在讲除号运算符时遇到的——算数异常。


见以下代码

在这里插入图片描述


我们在分子的位置出现了0,来运行以上代码。

运行结果:

在这里插入图片描述

2.数组下标越界异常


在数组篇我们也提到了数组越界的问题


我们来看以下代码:


在这里插入图片描述

运行时结果:


在这里插入图片描述

查看异常的信息

在这里插入图片描述

3.空指针异常


空指针异常也在之前的学习中经常出现


看以下代码


在这里插入图片描述


  我们将 array 数组置为 null ,之后再去访问这个数组,就出现了空指针异常。


运行结果:

在这里插入图片描述


  这些异常都是需要我们在平时所积累出来的,

  所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制.


关键字 “运行时”

  有些错误是这样的, 例如将 System.out.println 拼写错了, 写成了 system.out.println. 此时编译过程中就会出错, 这是 “编译期” 出错.

  而运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误.


4.异常的处理方式


  异常的种类有很多, 不同种类的异常具有不同的含义, 也有不同的处理方式.(在此了解即可)


防御式编程


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


LBYL: Look Before You Leap.
在操作之前就做充分的检查.

EAFP: It’s Easier to Ask Forgiveness than Permission.
“事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理.


其实很好理解,打一个非常形象的比喻来理解啊:


比如说有一个你非常喜欢的女生,你想要去拉她的手,那么有几种方式呢?

第一种方式 LBYL:问一下:我能拉你的手吗?


这就是操作之前仔细检查。


第二种方式 EAFP:直接先把手拉上,她要是甩开了再说其他的,要是没拒绝就拉着呗。


这就是”事后获取原谅比事前获取许可更容易“. 也就是先操作, 遇到问题再处理.


了解即可,不用特别去记忆。


5.异常的好处


我们看一下,上述的两种风格在处理代码时究竟是怎样的呢?

我们先给一个特定的场景啊,处理王者荣耀游戏开局时的异常代码


LBYL 风格的代码(不使用异常)
在这里插入图片描述


  代码的每一步执行完都要进行检查,确认正确才能进行下一步。这就是在操作之前做检查。


EAFP 风格的代码
在这里插入图片描述
在这里我们能体会到Java 风格的代码异常处理


在这里插入图片描述


  对比两种不同风格的代码, 我们可以发现, 使用第一种方式, 正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱. 而第二种方式正常流程和错误流程是分离开的, 更容易理解代码.


二、异常的基本语法


  接下来,我们就正式开始了 Java当中 处理异常的 基本语法讲解了。


1.基本格式


Java当中 处理异常的基本格式


在这里插入图片描述


2. 是否处理异常对程序的影响


我们来看一组代码:


在这里插入图片描述


  这组代码中,在代码执行的第二步中,我们存在着数组越界异常,那么第三步的 “hello” 是否会打印呢?


我们运行程序,看结果

在这里插入图片描述


  “hello” 并没有打印,那么这是为什么呢?此时程序出现异常了,而当程序出现异常时,那么代码将不会被执行。


  那么我们还是想执行这个“hello”,那么我们该怎么办呢?


我们就将代码写成以下格式的代码:

在这里插入图片描述
运行结果;

在这里插入图片描述

  hello 也成功进行了打印。也就是说,在这种方式下,代码抛出异常,捕获异常之后,代码将继续向后执行。


  那么又有同学说了,我们定义的那个 e 没有用到啊,我们再来看 e 的用处


在这里插入图片描述
e.printStackTrace — 打印出现异常栈的追踪

我们来看这时程序运行结果

在这里插入图片描述


  这时就把出现异常的栈的位置打印出来了,这就是 e 的一个作用吧


  那么又有同学来问了,为什么之前的 “hello”无法打印出来,之后的try…catch 捕捉异常之后能够打印出来呢?


在这里插入图片描述

3.用 try … catch 需要注意的问题


1.  在 catch 块中,一定要捕获相对应的异常,如果程序抛出的异常在catch 块当中,不能被捕获,那么就会交给 JVM 处理。


看以下代码:

在这里插入图片描述


  在catch 块当中并没有捕获到 数组越界异常,我们来看一下运行结果。

在这里插入图片描述

直接交给 JVM 处理,程序终止,不在向下执行。


2.可以通过catch 捕获多个异常


我们可以在 try之后 跟上多个 catch 来捕获异常,如以下代码:

在这里插入图片描述

运行程序结果如下:

在这里插入图片描述


  我们知道,所有的异常都继承于Exception,那么有人问了,我们可以直接捕获一个Exception的异常吗?


我们来试一下,


  我们知道捕捉异常的顺序是按照代码书写的顺序执行的,大家看一下这段代码

在这里插入图片描述


当Exception 这个父类异常放在开头,那么下面的捕捉异常则进行报错。


  如果这样写,那么Exception 后面所有的异常都失效了,为什么呢,因为不管 try {} 里面是什么异常,都会在Exception 这一步进行捕获到,后面的捕获异常自然失效了。

  所以当我们在前面写了 Exception 这个捕获异常时,后面就不要在进行捕获其他异常了。


  那么又有同学说了,我们为了省事,那么我们以后都捕获 Exception 这个 异常不就好了?


  当然不行,在这种情况下,我们不能区分我们捕获到的是什么异常。所以,不建议大家直接捕获到一个 Exception 的异常。

3.  不建议大家直接捕获一个 Exception 的异常


  有些同学想要省事,想要将两个异常合并成一条进行捕捉,我们只需要将两个异常用 | 进行连接即可。


给个示例:

在这里插入图片描述


看一下运行结果:

在这里插入图片描述

成功运行。


总结


1.  在 catch 块中,一定要捕获相对应的异常,如果程序抛出的异常在catch 块当中,不能被捕获,那么就会交给 JVM 处理。

2.  可以通过catch 捕获多个异常

3.  不建议大家直接捕获一个 Exception 的异常

4.  可以用 | 同时处理两个异常,如上例。


4.关于异常的处理方式


异常的种类有很多, 我们要根据不同的业务场景来决定.


对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果

对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿

对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试.

  在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息.


关于 “调用栈”


  方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述. 在 JVM 中有一块内存空间称为 “虚拟机栈” 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.


5.finally 的使用


我们再来看一下异常基础语法的学习

在这里插入图片描述


我们看到了try…catch 之后还可以跟着 finally

那我们来说一下 finally 的特性


  不管 这个代码 是否抛出异常,finally 的 内容都会被执行。所以 finally 经常来做一些善后的内容。比如:关闭资源


我们来看一下这一组代码

在这里插入图片描述

考一下大家,这组代码执行的结果是什么呢?

运行结果如下:

在这里插入图片描述

打印结果为2,这是为什么呢?

首先我们需要明确的一点是,finally 的内容一定会被执行。


我们分析一下:

  在try 块当中,在打印 array[4]时出现异常,后面的return 语句就不再执行了,所以最后执行 finally 块,返回2.


我们再来看一个代码示例:

在这里插入图片描述

这组代码执行的结果是什么呢?


运行结果:

在这里插入图片描述


最后返回了2.


这又再次明确了一点:


finally 的内容是一定会被执行的


finally 的使用 总结


1.finally 块当中的代码终究会被执行的

2 .不建议在 finally 当中出现 return 语句.


6.异常处理流程


在这里插入图片描述


  好了,到现在,我们算是讲清楚了 try…catch…finally 及异常处理的流程等问题,那么大家以后在写代码的过程中,一定要记得去使用 try…catch,不能一味的交给 JVM 来处理它,好了我们开始下一块内容——抛出异常。


7.抛出异常


  除了 Java 内置的类会抛出一些异常之外, 程序猿也可以手动抛出某个异常. 使用 throw 关键字完成这个操作。


  throw 一般抛出一个你想要抛出的异常(或者自定义的异常)


(1)throw 的使用


我们来看代码示例:

在这里插入图片描述


  我们用 throw new 了一个算数异常,为什么要 new 呢? 因为算数异常本身也是一个类,也要实例化。


我们来看运行时结果;


在这里插入图片描述


  成功的抛出异常了,但是这样写有一个不好的地方。我们抛出了一个异常但是呢,这个异常我们只是抛出了但是并未处理,所以最后程序出现异常后交给JVM处理,程序最后终止。


(2)声明异常


  对于我们调用devide 方法的人来说啊,如果 devide 方法的内容很多,我们就看不出 devide 会抛出一个异常。那么为了让调用devide 方法的人知道,我们调用这个方法会抛出这个异常,一般情况下,我们会给这个方法进行声明异常。那么怎么声明呢?


通过throws 声明这个方法会抛出一个异常


下面我们来看代码示例:

在这里插入图片描述

  方法调用者知道了调用该方法可能会抛出 算数异常,就用上了 try…catch 来捕获异常。


运行结果:

在这里插入图片描述

(3)小结

在这里插入图片描述

三、Java异常体系


那么异常到底有多少种?要了解这个问题,我们就需要知道Java的异常体系了。


在这里插入图片描述


上图并没有将所有的异常都列举出来,只是大概演示一下。


在Java当中,我们所看到的异常,其实也就是对应着一个类。


  由上图中我们可以看到,整个Java 异常体系 都是继承于顶层类 Throwable,那么 Throwable 就是所有异常、错误的父类。


对于Throwable 来说,直接继承这个类的有两个子类,


Error ( 错误 ) 和 Exception(异常).


我们来看一下 jdk_api 帮助手册中对 Throwable 的解释

在这里插入图片描述


我们这篇讲的是 异常,怎么又出现一个 错误Error呢?

我们也来认识以下Error

比如说我们写一个代码:


在这里插入图片描述
运行之后出现以下结果:

在这里插入图片描述
我们来对比一下:

在这里插入图片描述
异常是以Exception 结尾的,而错误是以 Error 结尾的。

对于Error 来说——这种错误一定得由程序员自己解决。

而对于Exception 来说——异常时可以由程序自己解决的。

而异常又有以下划分

在这里插入图片描述
  运行时异常 Runtime Exception就是我们上面提到的 算数异常、数组越界异常、类型转换异常等等,那么就有同学问了?


什么是运行时异常?

运行时异常就是在程序运行的时候抛出的异常

什么是编译时异常?

编译时异常就是在程序编译时抛出的异常


如果一段代码可能抛出 受查异常, 那么必须显式进行处理.


显式处理的方式有两种:


a) 使用 try catch 包裹起来
b) 在方法上加上异常说明, 相当于将处理动作交给上级调用者

别忘了 IDEA 神奇的 alt + enter, 能够快速修正代码.

小结

在这里插入图片描述


四、自定义异常类


  Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.


我们来实现一个简单的自定义的异常类


  首先 我们要自定义一个异常类 同时继承一个父类异常

在这里插入图片描述
那么这个我们自定义的异常怎么用呢?

下面我们来看

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

在这里插入图片描述

这就是我们自定义异常的使用。

我们再来一个代码示例

下面我们给一个真实的业务场景


例如, 我们实现一个用户登陆功能.


在这里插入图片描述


  此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们业务相关的异常类.


在这里插入图片描述


此时我们的 login 代码可以改成


在这里插入图片描述


自定义类的注意事项


1.自定义异常通常会继承自 Exception 或者 RuntimeException

2.继承自 Exception 的异常默认是受查异常

3.继承自 RuntimeException 的异常默认是非受查异常.



  好了今天的知识就分享到这里,希望大家多多练习,熟练掌握,感谢大家的欣赏与关注!!




谢谢欣赏!




完!

以上是关于Java基础语法—— 认识异常的主要内容,如果未能解决你的问题,请参考以下文章

Java 基础语法学会异常处理,祝你国庆快乐

Java 基础语法深度剖析 Java 中的数组

2018java基础面试题

基础01

Java 笔试面试 基础篇 一

java基础学习_java基础语法(上)01_day02总结.txt