Java的异常

1.关于异常

  异常的概述

        异常!!!
       
在我们生活中是几乎随处可见的,比如说我们生病了,就是身体出现了异常!
 

既然身体出现异常,是否解决就要看它的异常程度。就比如说发烧,感冒之类的,我们就不得不通过吃药去解决好,采取必要的措施去处理异常
 

        而在我们的程序中,尤其是学习的过程。出现异常和处理异常是必不可少的环节,而出现的异常的情况也有在编写代码时出现,也有在运行时出现。

        Java中,将程序执行过程中发生的不正常行为称为异常

        我们在编程的过程中,主要会遇到一下两种异常:
        Error:是指在Java虚拟机中无法解决的严重问题。比如说:JVM的内部错误、资源耗尽等                       ,比较典型的有StackOverflow(栈溢出),OutOfMemoryError(内存溢出)。一般不                     会编写特定的代码去处理这样的异常。
Exception:因编写原因,或者外在因素而导致发生的一般异常,可以通过别写一些特定的代码去                      解决,使程序继续运行。如:空指针异常,算术异常,网络连接异常等等。

异常的体系结构

        异常的种类有很多种,为了能将规范表示和使用,java里也有一个专门将它们分类的体系结构:

可以看得出,在这个体系中,Throwable是这个体系结构的顶层父类。而Error和Exception就是从这里面派生出来的。

  异常的分类 

        关于Exception异常,我们可以分为受查异常(编译时期异常)和非受查异常(非编译时期异常)。

        编译时期异常,就是我们在写代码时就会出现的错误,导致代码无法运行(但不是指语法错误导致的无法运行,比如:没加分号,括号)。例如:

此时发生的就是编译时异常,由于调用clone方法返回的类型时object类,而我们的person2初始化是Person1类,所以无法成功赋值,导致发生了CloneNotSupportedException异常,最终我们的代码无法运行。

        运行时异常

其实也就是非受查时异常,是在进行代码运行过程中,或者说运行代码之后才发生异常。在编译过程不会发生异常,报错,可以正常运行,但是运行结果无法输出。比如最常见的空指针异常

    public static void main(String[] args) {
        int[] arr = null;
        System.out.println(arr.length);
    }

 运行结果:

除此之外还有常见的
计算异常(ArithmeticException)
数组越界异常(ArrayIndexOutOfBoundsException
输入不匹配异常(ArrayIndexOutOfBoundsException)
等等,这些异常都属于运行时异常。这些异常通常都是RunTimeException以及其子类对应的异常。

2.异常的处理

        在Java 中, 异常处理主要的 5 个关键字: throw try catch final throws

2.1 异常的抛出

在编写程序时,如果程序中出现错误,或者不希望出现的情况被输入。


在java中,我们可以通过throw关键字,抛出一个指定的异常,从而终止程序运行并将错误的信息告知给运行者。比如我不必希望输入“10”:

当我们输入10之后,就会出现异常

关于throw:

1. throw关键字的使用必须在方法体的内部。
2. 抛出的异常必须是Exception或者Exception的子类对象。比如说还可以抛出空指针异常:

运行结果:


可以看得到,异常在第14行,也就是我们抛出异常的地方。

3.如果被抛出的是编译时异常,我们就必须马上处理,否则代码将无法编译。当我们设置的异常一旦被抛出,后面的代码将不会再执行。

2.2 异常的捕获

既然我们想要将异常处理,我们就必须将异常给捕获从而进行处理。而捕获异常的方式通常有两种:
        1.异常声明throws
        2.通过try-catch-finally方式

2.2.1 异常声明throws

这种处理方式会放在发生异常的方法的参数列表后面,像这样

修饰符 返回类型 方法名 (参数列表)throws 类型1Exception,类型2Exception...{

        /...../

}

        这种处理方法的用法是:当方法中在编译过程时抛出了异常,用户又不想处理该异常,提醒调用者来处理。即当前异常编写时不提醒,但是会在调用时,提醒调用者处理。

需要注意的是:声明的异常必须是Exception或者是Exception的子类,而且必须写在方法的参数列表之后。当方法内部抛出多个异常时,throws也必须加上多个异常,并且用“,”隔开。(如果抛出的多个异常都是来自于同一个父类,只声明一个父类的异常就可以了)

2.2.2 try-catch-finally捕获并处理

        throws的处理方法只是让编译时异常不在报错,从而让代码运行。但是异常的地方还是没有被解决掉。最后还是会导致代码无法正常运行完毕。所以我们就可以通过try-catch-finally的代码段解决出现的异常。

基本语法:

try{

        /*可能出现异常的代码*/

}catch(要捕获的异常类型 e){

        /*如果在try当中出现了异常,并且被这个catch捕获到对应异常类型,将会运行这段代码里面的内容。*/

        /*但如果没有捕获到异常,或者try中没有发生异常,这段代码将不会运行,或者交给下一个catch处理,如果都捕捉不到,就会交给JVM处理。*/

}catch(异常类型 e){

        /*对异常处理*/ 

}.....finally{

        /*此处的代码一定会被执行到*/

}

 

运行结果:

 这种捕获的方式,更多需要注意的地方在try部分的代码段:

        1. 在try当中,如果有在一处地方发生了异常将不会在继续try里面的剩下的代码,而是到catch中处理异常。

结果还是会和上面一样

 2.try当中捕获到一个异常之后,将捕获不到其它异常,一个try-catch代码段只能捕获到一个异常,因为发生异常时,try已经不会再继续执行。

运行结果:

3. 同样当我们不确定try代码中会发生什么样的错误时,我们可以采取多个catch来捕捉异常。 

运行结果:

如果我们多次捕获异常后处理方式都一样,可以将多种异常写在一起

----------------------------------------------------------------------------------------------------------------

但是如果我们捕获的异常具有父类子类关系时,必须注意它们的捕获顺序

ArithmeticException | NullPointerException都是Exception的子类,所以会发生错误。

所以我们要将父类的异常放在子类之后,才能正常运行

所以,我们也可以用这种方法来捕获我们代码中未知的错误,当我们用子类的异常去捕获时捕获不到,可以用Exception来放在最后来捕获,使代码正常运行,不被JVM处理。

2.2.3 关于finally

        在写程序时,有些特定的代码,无论是否发生异常,都必须要执行的,比如说程序中打开的资源:网络连接,数据库连接,IO流等,在程序正常或者异常退出的时候,都必须要对资源进行回收。有的代码会因为异常的出现而导致执行不到。而finally部分就可以解决这样的问题。

        在finally里的语句无论怎么样都会被执行。不论是代码正常运行,还是发生了异常。

运行结果:

可以看见,没有异常时,所有代码都会被正常执行。

但是当代码内出现了异常

运行结果:

代码将会在异常出现的位置开始停止执行其它语句并报错,但是finally里的语句仍然可以运行。这就是finally的特别之处。

所以但我们想关闭一些程序时,或者回收一些资源时,最好的办法就是写在finally语句里,防止出现异常后,不能正常回收或关闭。

---------------------------------------------------------------------------------------------------------------------------------

除此之外,finally语句还有一个特殊的地方。 

当我们运行这段代码,并输入10的时候,结果会返回什么呢?

运行结果:

结果竟然是1000。也就是说try里面并没有执行的return,而是执行了finally里的return。

那再通过一段代码进行比较:

运行结果是:

通过对比,可以看得出,finally与return的执行顺序是,先执行finally部分,才会执行try-catchi里的return。

总结finally与return的关系:

        finally的执行时机是在方法返回之前执行的,并且一定会执行。当return在try-catch部分时,finally会先执行完自己的代码,再执行try-catch里的return。而当两个部分都同时又return时,会再执行完其它代码之后,直接在finally里执行return。

所以我们一般不在finally里写return语句。

2.3异常处理流程

了解上面之后,我们可以得出我们处理流程的大致过程了!

1. 先将可能会产生异常的代码写到try部分。

2.如果try中的代码出现异常,那么try中的代码会从异常出停止执行,并看出现的异常与catch中的     异常比较是否匹配。

3.如果异常匹配,将会运行对应的catch里的代码。

4.当在出现异常的方法中无法处理,将会把异常一直向上传递。比如在这段代码中:

可以很明显看出会发生算数异常,但是我们的catch无法捕获和处理这个异常,而运行结果是这样的:

可以看到,它会在getData方法中先尝试处理,处理失败后报错,交给main方法处理。但是main方法也无法处理,最后交给JVM处理,所以最后就终止了程序。

5.无论是否能找到匹配的异常类型,finally中的代码都会在该方法结束或者返回结果之前被执行。

6.如果一直都没有合适的代码来处理发现的异常,最后就会交给JVM来处理,此时程序就会异常终止。

3. 自定义异常

        在java中,虽然它本身就自带了一套异常体系,但是还是不足以满足我们在各种各样的场景下,会发生各种各样的异常。所以自定义异常,就是用来设计符合我们要维护的实际的异常情况。

3.1 自定义异常的定义方式

1.一般我们的自定义异常,都是来自于已有异常类的继承或者扩展。也就是继承Exception和RunTimeException。

        当我们自定义的运行是异常时,继承的是Exception。
        自定义的是编译时异常时,继承的时RunTimeException。

2.实现一个带有String类型参数的构造方法。该参数用于接受出现异常的原因。

3.2 自定义异常的设计

我们先设计一个登录系统。


我们在这段代码可以自定义两个异常,账号异常(UserNameException)和密码异常(PassWordException)。

先创建两个类来创建自定义异常

在利用Generate,将构造方法自动生成。


然后我们再回到登录系统中,如果我们使用throws将我们自定义的异常捕获,就需要再方法参数之后加上异常类型,并在代码中用throw来将异常抛出。

如果我们用try-catch-finally的代码段捕获,就不用再方法的参数列表之后声明异常类型,直接在try中写出可能会抛出异常的代码。再用catch捕获异常即可。

代码中e.printStackTrace();是用来提醒出现异常的地方。根据上面的代码运行结果为:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值