这是我参与8月更文挑战的第 21 天,活动详情查看:8月更文挑战
何为异常
在程序运行中,通常会是由于某一步的参数传递,或者是参数获取,出现了问题,然后导致整个应用崩溃
比如一个下方示例的方法 caculate(int),将用户传入的数作为一个除数,并覆盖为 1/i,但是,由于方法内部设计不到位,调用者传入了一个出乎意料的参数 0,然后,BOOM,一堆错误弹窗,以及,程序挂了。。
public void caculate(int i){
i = 1 / i;
}
异常抛出
当异常发生时,我们希望在当前方法中专注业务,然后将异常向上传递,让上一级方法负责处理当前方法抛出的异常
一般来说,通过 throws 向上传递抛出的异常,要大于或等于当前的异常
所谓的异常抛出,就是当前方法向上一级的调用者发出警告,“我可能(或必定)会抛出某个(或某些)异常,需要你这里明确得知这个异常并需要处理”
void a() throws ExceptionA{}
void b() throws ExceptinoB{}
void c() throws Exception{
a();
}
异常捕获
以上一步的代码为例,我们在另一个函数中调用这些抛出了异常的方法,需要以 try catch 的方式对这些抛出异常的方法进行包裹,然后在 catch 语句块中对方法调用出现的异常进行捕获
void d(){
try {
b();
c();
} catch (Exception e) { // 捕获到 try 中的异常
// 在这里,在上一步的异常,实际发生了的情况下,需要如何做
} finally {
}
}
try
尝试调用某些方法,这些方法可以不抛出异常,也可以抛出异常
catch
如果上一步的代码块中确实存在一个(或一堆)抛出的异常,在 这里进行捕获
多种捕获方式
1、叠罗汉式
在 java 中,异常的匹配方式是从上往下进行的匹配,只要能匹配到任意一个,就会去执行对应的 catch 中的代码块;所以,如果异常之间存在继承关系的话,子类异常优先排在最前面
如果继承结构如下,上面父类,下面子类
ExceptionC
|- ExceptionB
|- ExceptionA
那么建议的捕获顺序就是如下:
catch(ExceptionA A){
}catch(ExceptionB B){
}catch(ExceptionC C){
}
2、同级 catch 异常 “逻辑或”
一般使用的场景,捕获到多个异常,但需要同一套异常处理的逻辑
catch(ExceptionA A | ExceptionB B){
}
3、一把梭哈式
正如名字一样,不管他们具体报了啥异常,直接使用他们的公共父类 java.lang.Exception 进行 catch,这种建议慎用,当然写一些 demo 代码的时候可以随意霍霍(提桶警告.jpg)
catch(Exception e){
}
finally
最后的 finally 代码块,一般用来做清理收尾的工作,即 try 不报错的话,最后执行;若 try 报错的话,等 catch 捕获的异常顺序执行完成之后,在执行 finally 中的代码
细讲
在 java 的异常继承树如下
Throwable
|- Exception
|- RuntimeException
|- ....
|- Error
去 IDE 中看了下 Exception 的子类,家族是真的庞大。。。
在这里,额外点出,需要注意 RuntimeException,因为 RuntimeException 及其子类异常,在抛出时,上一级方法不需要显示的捕获,作为代价,这类异常一旦出现,程序直接原地升天
同时,点名匹配 MyBaits sql 相关的异常,追溯起来也是继承自 RuntimeException,因为我 junit 调试 mapper 的时候,给莫名炸哭。。。
当然,RuntimeException 这种特殊的异常,我们给相关的方法包上一层 try catch 就能捕获
不过,在 java 语言的设计当中,RuntimeException 表示的就是程序运行期间,可能会出现的异常,不必捕获。如果需要,我们可以设计一些继承自 RuntimeException 的异常来使用
在java 的异常系统中,java 将异常的用途用名称标识,如 RuntimeException 就标识运行时异常,FileNotFindException 文件找不到异常,等等,见名知意,我们习惯通过定义异常的名称来传递异常的用途
异常的常规使用
throw new Exception("这里是异常的详细描述");
throw new MyException("这里是异常的详细描述");