前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >碰见异常 你是选就地正法 还是甩锅大法 码思客

碰见异常 你是选就地正法 还是甩锅大法 码思客

作者头像
用户5745563
发布2019-07-04 11:41:11
4850
发布2019-07-04 11:41:11
举报
文章被收录于专栏:码思客
java零基础入门-高级特性篇(九) 异常 中

上一节讲到了检查异常,这种必须处理的异常到底该怎么处理呢?通常的处理方式就是捕获异常或者抛出异常,捕获异常就是在异常出现的时候当场解决,而抛出异常则是把锅甩出去,把异常往上层抛出,让上层逻辑来解决它。处理异常有专门的关键字,java中的异常家族里有以下几种关键字,try、catch、finally、throw、throws,下面来分别介绍它们。

捕获异常

捕获异常就是当场就地正法,使用try和catch关键字来处理异常。try用来监视代码逻辑的运行,如果没有异常,那么程序会一直运行到结束,而一旦发生异常,并且在try的监控范围之内,那么程序就会跳转到catch部分,运行catch里面的代码。如果没有捕获异常,程序会直接结束,所以捕获异常可以给我们一次挽救程序异常停止的机会,就算不能挽救,也至少可以知道为什么程序会出现异常。

try-catch

上面这个try-catch结构就是基本的捕获异常结构,try后面的程序就是正常的逻辑代码,catch后面是如果发生了异常需要执行的代码。需要注意的是,在出现异常以后,不会继续执行程序,而是直接跳到catch部分执行代码,所以这里输出完第一个打印语句以后就马上输出了异常信息。

e是Exception的对象,调用Exception父类Throwable的方法printStackTrace(),输出异常信息,输出异常信息有多种方式,printStackTrace()是一种,这种方式输出的是最详细的错误信息,包括出现异常代码的行号,异常信息和异常原因,这种方式适合调试程序,找到错误。还有一种getMessage(),这种输出只会输出异常的信息,比上面一种方法输出的信息要少,这种方式适合记录日志,将错误的信息作为日志记录下来,以便需要的时候排查问题。

多个异常的捕获结构

上面的例子是使用Exception捕获的异常,其实理论上来说,应该使用最准确的异常来捕获,由于Exception是所有异常的父类,所以使用Exception没有问题,但是最适当的方式是使用FileNotFoundException来捕获这里的异常。为什么要用子类来捕获异常?因为使用子类捕获异常可以将异常处理的更加精细,比如下面这个例子。(里面流和反射的知识可能没有学到,但是此处只需关注异常即可)。

使用子类捕获

当一段逻辑中有可能出现多个异常需要捕获的时候,如果直接使用Exception,那么只能执行一个异常逻辑,而不能将不同的异常区分开。这样就导致无法根据不同的异常进行不同的异常处理。当一段逻辑中出现多个需要捕获的异常的时候,可以在try后面接多个catch,分别对不同的异常类型进行捕获。

同时使用

Exception和Exception的子类在捕获异常的时候是不冲突的,但是子类的捕获必须在父类之前,如果第一个catch的是Exception,那么他会直接捕获所有异常,不能单独处理其他异常了。异常捕获的顺序是按照异常出现的顺序来的,如果首先出现的是文件找不到异常,那么会被FileNotFoundException捕获,如果首先出现的是ClassNotFoundException,那么会被Exception捕获。

有finally的结构

讲完try和catch关键字以后,再来看另一个关键字finally。在处理异常的时候,try关键字是必须出现的,有了try关键字,程序才会在try所包含的代码块中捕获异常,而catch和finally是可以任意出现一个的,也可以两个同时出现。finally的特性是,不论在catch中是否出现异常,finally中的代码都会被执行。因为有一些代码如果写在try中,如果出现异常,那么这些代码是可能不会被执行的,如果写在catch中,如果不发生异常也不会执行,所以需要一个地方来写无论是否出现异常都会被执行的代码。

finally

finally在处理资源的时候非常有用,比如IO,网络,数据连接等等,因为在使用这些资源的时候需要在代码中手动的回收,但是如果发生异常就不会执行到回收资源的代码,所以在finally中回收资源是一种很好的选择。

使用finally需要注意的几个地方:

1.如果有一个或多个catch关键字的话,finally要出现在最后一个catch之后。顺序如果有错误会发生编译错误。

2.不建议在finally里面使用return关键字。

finally中的return

在finally里面是可以使用return关键字的,但是会导致结果与预期不符合。比如上面这个例子,上例中其实是不检查异常,可以捕获也可以不捕获,这里为了说明finally就捕获异常了。这里预期返回的是两个参数的商,程序运行到try中的return是不会马上结束方法的,因为后面有finally语句,而finally语句中也有return,最后的结果就是finally中的return导致try中的return无效。无论程序是否发生异常,方法预期返回的结果都被改变了,返回的不是程序希望得到的两个参数的商,而是一个与参数无关的字符串,所以通常不建议在finally中使用return关键字。

final ,finally 和finalize

这个地方要指出的是,这几个关键字八竿子打不着关系,但是经常会有外行题目问这几个关键字有什么区别。这里简单说一下。

final 定义的变量,初始化变量后不可修改。final定义的方法不可以被覆写。final定义的类不可以继承。

finally用于异常结构,不论是否发生异常,都会运行finally中的代码。

finalize用于定义垃圾回收器应该执行的操作。

抛出异常

捕获异常讲完了,轮到抛出异常了。前面说了检查异常,有没有想过,为什么检查异常就必须处理呢?因为在定义类,方法的时候,源码已经将异常抛出了,所以你在使用类的时候就必须处理它,要么捕获,要么抛出。前面例子中

FileOutputStream out = new FileOutputStream(file);

会有一个FileNotFoundException类型的异常必须处理,来看看FileOutputStream这个类的构造器。

抛出异常

什么是抛出异常?

抛出异常就是遇到检查异常,并没有捕获异常直接处理,而是将异常交给调用方处理。

为什么要抛出异常而不是直接捕获?

因为设计上的需要。当我们在写一个业务的时候,碰见异常最好的方法就是捕获并处理它。但是如果写的是一个公共的工具方法或者是父类,抽象类等需要将业务进行抽象的时候,并不能预见到具体的业务是什么,所以不能直接给出解决方案,这时候就需要将异常交给调用方,在使用者具体使用的时候,再来捕获该异常,根据具体情况确定具体的处理方式。

异常具体是怎么抛出的?

异常的抛出

首先在一个需要抛出异常的地方将异常往上一级(方法的调用者)抛出,然后上一级还可以继续往上一级抛出,如果到最后都没有被捕获,该异常会被抛给jvm,jvm也没法处理异常只能把异常信息打印出来。

这个过程就像出了问题,开始甩锅一样。方法A出了问题,自己可能没有办法处理,就把锅甩给了方法B,方法B一看这个我也没法解决啊,转手又甩了出去,最后这个锅被甩给了老大哥JVM,JVM老大哥看到异常也只能干瞪眼,没有办法最后只能把异常信息打印出来,谁写的代码谁来认领一下,错误给你看了,自己想办法去解决。

抛出异常

java使用关键字 throws 抛出异常,throws后面跟上异常的类型,跟catch的捕获类型差不多,定义什么类型的异常就会抛出什么类型的异常,如果直接抛出Exception,那么就是抛出所有的异常类型。跟catch可以捕获多种异常类型一样,throws也可以抛出多种异常类型,这样就可以让上一级的代码根据不同的异常类型分别进行处理。如果只抛出Exception类型的异常,上一级就无法对异常进行精确的控制了。

抛出多个异常

使用throws同时抛出多个异常的时候,使用逗号将多个异常分开。throws这种抛出异常的方式可以看做是一种被动式抛异常,因为throws抛出的异常可能发生也可能不发生,java中除了throws抛出异常,还有一种主动式抛异常,下面来看看什么是主动式抛异常。

throws 和 throw

主动抛出异常的关键字是throw。和throws只差了一个小写字母s,这里需要重点区分开两种异常抛出方式的区别。

throws:1)抛出的是类,在方法后面写的是异常的类名 2)可以同时抛出多种类型异常 3)throws抛出的异常不一定会发生 4)在方法名处抛出

throw:1)抛出的是异常类的实例 2)只能抛出一种异常 3)抛出的异常一定会发生 4)在方法内部抛出

throw用在抛出不检查异常的情况比较多。使用throw可以将代码的逻辑补充的更加完整,因为某些异常在特定的情况是需要根据业务逻辑来判断是否抛出,在特定的情况下是可以确定异常的,而不是像throws不确定是否会出现异常。这种情况下就可以使用throw在方法体中抛出异常。

throw

上例中,假设用户需要输入两个数字,然后计算两个数字的商。用户输入是不确定的,但是一旦用户将intTest2输入为0,代码逻辑可以确定这里肯定会有一个异常,那么可以直接使用throw来抛出这个异常。一旦在调用方法时捕获到该异常,也可以确定异常的信息,比如上例中可以将捕获到的信息直接反馈给用户,第二个数不能为0。

需要注意的是throw只是抛出异常的方式比较灵活,可以在代码逻辑中抛出异常,而抛出异常以后,上级的处理逻辑和throws是一样的,要么继续往上级抛异常,要么捕获异常。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码思客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档