Java—认识异常 ( ̄▽ ̄)~*

目录:

一、异常的概念和体系结构:

1、异常的概念:

 2、异常的体系:

3、异常的分类: 

二、异常的处理:

1、防御式编程:

  1)、 事前防御型(LBYL) :

 2)、事后认错型(EAFP):

2、异常的抛出:

3、异常的捕获:

1)、声明异常throws:

※ 2)、try-catch捕获并处理异常:

 3)、fianlly

4、异常的处理流程:

三、自定义异常:

总结:


一、异常的概念和体系结构:

1、异常的概念:

     我相信,在我们在运行代码的时候,总会出现一些问题。所出现的这些问题,我们在Java中,我们将程序在执行中,出现的不正常的行为,称之为异常。

     那么有几种异常在我们写代码的时候经常遇到,比如:

1、算数异常:

我们来看看,什么叫做算数异常呢,直接看代码:


2、数组越界异常: 

       我们在使用到数组的时候呢,是不是经常的遇到或听到数组越界这个词,那么它的异常是什么样的呢? 我们来看看啊,直接看代码:

   当然在这个异常之后,再执行代码就不会再执行了。 


3、空指针异常: 

    对于空指针还是很好理解的,我们看看代码,就可以看懂了:     对于这个之后要是有代码,也是不会执行的,所以在这里呢,我呢就不在进行举例了,和上面是一样的。 

   所以从这几个例子就可以看出,对于异常来说呢,对于不同的异常,就会出现不同的结果。

   在Java中不同的异常,都有其对应的类来进行描述。 


 2、异常的体系:

   对于异常来说,它有很多的种类,所以在Java中维护了一个异常的体系结构。我们看图片来看看是什么样的


么这些都是什么意思呢,我知道你很急,但你先别急,现在我们来一一介绍:

Throwable:是异常类型的最顶层,它呢会派生出两个子类,这两个子类都是非常的重要的。

Error:这个就是Java虚拟机无法解决的问题,比如呢:内存的资源耗尽,JVM的内部错误,这些呢都是Error,最典型的,也是经常发生的就是:陷入死循环,使资源耗尽而出现的异常。

Exception:这个就是当程序产生异常后,程序员可以根据代码进行解决的异常,就是这个。 

我们接下来看看。Exception这个异常的分支,都派生出的那两个子类都是什么意思。


3、异常的分类: 

1、运行时异常/非受查异常:

     这个异常呢,就是我们上面的那些算数异常、数组越界异常、空指针异常等。就是运行时异常,在不运行时不会报错,只有运行时才会报错。

2、编译时异常/受查异常:

        这个呢,就是在我们讲抽象类和接口的时候呢,讲的深度拷贝就是一个例子,我们来重新看一下:

   

   当然这种的就不是异常了,这个只是代码写的问题,不是异常。 

   那么我们在认识异常之后呢,我们就要想一想我们要是出现了Exception这种的异常的时候,我们要如何处理异常呢?接下来我们来看看:


二、异常的处理:

1、防御式编程:

      错误的代码是客观的,我们要在出错前提示程序员,让其知道是哪里出现了问题,对于实现这种方法的方式主要有:

  1)、 事前防御型(LBYL)

  这种的呢就是事前防御型的,处理异常的方法。

  这个方式是有缺陷的:正常的代码流程和处理错误的流程混在一起,使代码过于混乱了。

  我们接下来讲的就是我们Java中常用的方式。 

 2)、事后认错型(EAFP)

   我们在事情发生后认错,比事前确认可不可以做,更容易获取原谅,也就是先斩后奏。 

   在这种方法中呢,我们就要了解并且熟悉Java中的一些处理异常的关键词:try、throw、catch、finally、throws 这5个关键字。我们来看看如何使用的:

 还是同样对于微信的登陆:

        这里我们看到了5个关键词的其中其中两个,那么这两个是什么意思,并且对于那3个又是什么意思呢? 

        在了解这个之前我们先来了解 事后认错型的优点什么,为什么我们在Java中要使用这个来处理程序的异常。

      优点:正常的代码流程和处理错误的流程是分开的,这样呢会使程序员更加的关注正确的流程                   代码,使得代码更加的清晰,可以更好的理解代码的流程和步骤。

      接下来我们来看看它们5个的作用是什么,又是如何实现的。 


2、异常的抛出:

    在我们的程序出现错误的时候,这个时候呢,就需要把错误信息告诉程序员,出现了什么错误。

    在Java中呢,我们可以使用throw关键字,来抛出一个指定的异常对象,将其信息告诉我们程序员。我们来看看如何使用这个throw这个关键字: 

    那我们看到这时,就会有人有疑问了,我们抛出的异常和编译器给我们的异常爆出的不是一样的吗?我们又为什么要这么写?当然不一样了,我们看看,我们这个代码可以如何更改:    由此我们就可以知道,我们抛异常的代码要如何写了吧~我们来看公式:

      throw new XXXException("产生异常的原因");

   当然对于我们使用throw抛出异常的时候我们呢,有一些注意事项要进行注意一下:

• throw要写在方法体的内部。

• throw抛出的对象必须是Exception本身又或者是Exception的子类对象,但是我们在抛异常的      时候要特殊注意一下如果抛出的是RunTimeException本身又或者是RunTimeException子类        的时候,可以不做任何的处理,可以交给JVM来进行处理。

• 如果我们抛出的是编译时异常,那么我们必须要进行处理,要不然代码无法编译。

• 当我们throw异常后,其后代码不会再执行。我们来看看:

    现在呢,我们知道了对于异常的抛出,那么当我们抛出异常之后呢,我们是不是要捕获并且解决这个异常呢?那么我们接着往下看: 


3、异常的捕获:

  在我们Java中呢,我们有两捕获异常的方法:声明异常throws 和 try-catch捕获处理异常 

我们分别来一一了解一下:

1)、声明异常throws:

       这个方法是处在方法列表的后面,这个实在当用户不想解决这个代码的时候,我们可以使用这个方法,来把异常抛给方法的调用者来处理这个方法。就是当我们方法不想处理这个异常的时候,我们抛给方法的调用者来处理。

       这个方法我们之前见过,在我们介绍编译时异常的时候就使用过。我们还是看这个例子:

   那么假如我们也不想在调用者中进行处理呢?我们又要如何做到呢?我们可以继续声明异常:

   但还我们需要注意的是当我们交给JVM来处理的时候呢,就会终止程序了。 

当我们使用这个声明异常的时候呢,我们可以声明多个异常,比如:

     我们需要叫逗号来进行分割,但是呢我们需要注意的是,当我们声明多个异常的时候呢,如果出现父子关系呢,我们可以直接声明父类就可以了。我们来举个例子:

 

   继承IOEcepection这个异常类

   这里我们就可以非常容易理解这个父子类关系了 。

由此上面的这些实例我们就可以知道我们throws声明异常的语法了:

修饰词  返回值类型  方法名(参数列表) throws 异常类型1, 异常类型2, 异常类型3..... {

}

在我们使用throws声明异常的时候要注意的是:

     • throws必须使用在参数列表的后面。

     • throws声明的异常必须是Exception本身或者是Exception的子类。

我们接下来要介绍的try-catch方法是非常重要的,它使我们对于异常的捕获并且处理的方法。


※ 2)、try-catch捕获并处理异常:

      我们虽然在异常捕获的时候就说过,关于捕获异常有两种方法,但是到是当我们在了解throws方法之后呢,就知道了其并没有真正的处理异常,只是将异常声明,并且要抛给调用者来处理异常。所以当我们真正捕获异常的时候其实只有一个:try-catch方法。

try-catch方法:

try {

//可能发生异常的代码

}catch (异常类型 e) {

//对捕获的代码进行处理

}catch (异常类型 e) {

//对捕获的代码进行处理

}.......

我们先来看看这个是怎么使用的:

   执行代码的结果:

我们对于代码进行更改再看看结果如何: 

异常的处理方式: 

      在Java中我们有很多种的异常,不可能是每种异常都需要使用try-catch方法进行处理异常。我们呢要根据实际情况来决定。比如呢:

     • 对于比较严重的异常(比如 和内存相关的),我们应该直接让程序崩溃,防止产生更严重的后果。

     • 对于不是太严重的问题,我们可以捕获异常并通知程序员解决异常。

     • 对于有些可能会恢复的异常呢,我们可以尝试进行重试代码。

注意: 

     • try内当抛出异常之后,在异常后面的代码就不会执行。

     • 

     那么我们要怎样才能打印哪里出现的异常呢?很简单,我们直接在catch中实现: 

   这样做我们就可以得到错误的信息了。 

    • 在try当中我们可能会捕获很多的异常,那么我们必须要用catch多次接收,多次捕获

   这样我们就可以在捕获 数组越界异常 还可以 捕获空指针异常 了。 

    那么看到这里可能呢就会有一些疑问了,我们可以接收多个异常的话,那我们可不可以一次性捕获多个异常呢?比如这样:

我们来看看输出的结果:    所以我们不会捕获多个异常的,这个是需要注意的。

对于这个代码我们是可以进行简化的,当然简化的不是最好的,为什么呢?简化之后我们代码不应该更简洁了吗?为什么不是最好的?我们来看看:

     那为什么是不好的呢?这是因为,当我们打印处理.....异常的时候,那么我们是处理了 数组越界异常呢 还是 空指针异常呢 ?这就是个问题了。所以不是很建议这样去处理异常。但是也是可以写滴。

   对于这个简写,有的人是不是会这样写:

       如果是这样写的话,就更有问题了,对于Exception来说有很多的子类异常,我们使用这个的时候,就不知道到底是什么异常导致的了。我们再来看段代码:

     Exception是NullPointerException的父类,当我们捕获到Exception这个异常了,它的子类就捕获不到了,所以相当于后面的catch就是多余的了。不能所有的异常都用Exception来接收。

     但是呢,我们的代码可以这样写:

  这样就是先进行接收空指针异常,在之后再进行接收Exception的这个父类异常进行兜底。

 所以我们在catch是要先写子类再写父类,在有父子类关系的时候。 


 3)、fianlly

     我们把其余的四个关键词都进行了了解,现在我们只剩下finally了,那么接下来我们来看看对于finally是怎样进行使用的,又有什么作用。

    在了解之前,我们先来了解一下别的东西,在我们写程序的时候呢,我们肯定有一些特殊的代码,不论我们的程序能否正常执行,我们都需要执行这些代码,比如呢在程序中打开的资源:数据库,网络连接,在程序中不论是否正常,必须要对资源进行回收。在我们出现异常的时候就会退出try-catch方法,有些代码就不会执行,这时候就需要使用finally来解决之个问题。

 我们来看看代码:

    那么这个时候呢,就会有人问了我们的finally和在try-catch方法外面写的代码也可以执行,那么我为啥还要用finally呢?

    那么我们在来看这个例子就知道了,我们为什么使用finally代码。 我们来看:

      OK,我们可以看到这里的在try-catch之后的代码,没有被执行,内存流没有被释放,导致资源泄露。 但是fianlly里的代码还是被执行了。所以我们一般把对于资源的释放代码放在fianlly里,进行扫尾工作。我们来更改一下代码看看:

    我们还可以对于这个代码进行一定的优化: 

在这里我们有一个非常特殊的一个问题,我们来看:     fianlly呢是做的一个是善后工作,可以理解为,把10给覆盖了,但是我们不要这么写代码,这是不好的。


4、异常的处理流程:

   如果方法本身没有处理异常就会,向上传递,最后就会交给JVM,导致程序终止。

我们来看事例:

    先是程序本身有没有处理异常,如果没有,就交给调用者,再看有没有处理异常,再到main函数中看看有没有处理异常,如果还是没有处理异常,就交给JVM来处理,但是在这会终止程序 

所以我们总结可知:

     • 程序先执行try中的代码

     • 如果try中的代码执行出现异常,就会到catch中寻找和其出现异常匹配的异常

     • 如果找到了,就会执行catch里面的方法,如果没有找到的话,就会向上传递到调用者

     • 无论是否找到匹配的,finally的里的代码都会执行

     • 如果上层调用者也没有处理异常,就继续往上层调用,一直到main中,如果还是没有处理异常的话,就会让JVM来处理,这是会使程序终止。

三、自定义异常:

    在Java中呢,我们有很多的异常的类,但是呢,还是不能表示所有遇到的异常,所以这时候我们就要自己维护这些异常。

   比如我们现在有很多的软件是不是都有登录这个操作啊。但是呢,在Java中没有系统自带的登录异常,所以这时候呢,我们就要自己维护一个登录异常的操作。

   下面我们来看看,我们怎样来自定义登录异常的:

    我们可以看到,我们代码出现了错误,这是为什么呢?这是因为我们继承的是Exception,使其自定义的异常称为编译时异常/受查异常,这时候呢因为我们没有声明异常,我们不知道是哪种异常,所以我们要在抛异常的方法后面声明可能会出现的异常,才可以。我们来看: 在我们自定义异常的时候呢,我们有一些注意事项:

    • 在我们自定义异常的时候通常继承Exceprion或者是RuntimeException

    •  在我们继承Exception时候呢,我们自定义的异常称为 编译时异常/受查异常。

    •  在我们继承RuntimeException时候呢,我们自定义的异常称为 运行时异常/非受查异常。

    在我们集成RuntimeException的时候呢,如果我们在抛异常的方法后面没有声明异常,也是不会出错的。 但是呢,一般我们还是声明一下异常比较好。 


总结:

     OK,这次的分享就到这里就结束了,在这个之后呢,对与我们Java的基础就告一段落了,接下来我们就要进入数据结构的章节了,让我们敬请期待吧。拜拜~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值