Java的异常处理机制
简介
Java的异常结构图
从图中观察可得:
- 所有的异常类是从java.lang.Exception的继承子类。
- Exception类是Throwable的子类。Error也是它子类之一,
异常可能在如下几种情景下发生:
- 用户输入数据不合法。
- 找不到要打开的文件
- 网络在传输中断开
程序问题分类标准:
- 检查型异常 在编译时期出现,也称作为编译时异常,这些异常需要程序员根据提示处理好。
报错信息:
- 非检查型异常 又称作为“运行时异常”,常由 逻辑错误和API的错误调用导致。这类异常在编译时被忽略。
报错信息:
PS:我们可以发现,检查型异常在编写时就会有报错信息让你修改,并且编译没有通过。而运行时异常通过了编译,是在运行时报的错。
- Errors 一般是由运行环境产生的,在编译时期觉察不到,这类问题一般程序员处理不了。
注:异常可以处理,但是errors是不能处理,也就是说遇到error,程序一般无法自动恢复运行。
报错信息:
关于异常处理:我们一般只捕捉处理编译时异常,而对于运行时异常,因为它们常常是api调用方式不对(参数为空等),我们不会捕捉,而是让程序员发现后进行代码修改,如果强行处理运行时异常,可能会导致逻辑错误更加严重。并且,运行时异常占了绝大多数,处理起来会很麻烦,降低代码阅读性。尽管如此,的确有些特殊情况让我们处理,比如空指针异常。总之,不管你是怎么声明异常,想让你程序运行下去就用检查型异常,想让它停止,就用运行时异常。
常见的异常
1.非检查型异常:
2.检查型异常:
表格摘自:https://www.runoob.com/java/java-exceptions.html
异常的捕捉方法
1.try catch 语句使用
public static void main(String[] args){
try{
int num[] = {1, 2, 3, 4};
System.out.println(num[5]);
System.out.println("这是异常后的位置");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("异常发生,处理异常中---");
}
System.out.println("异常处理后---");
}
输出:
异常发生,处理异常中---
异常处理后---
注意:try块中发生异常后,异常位置之后的代码不会被执行,异常一旦发生,就会马上去catch中找对应的异常处理方法。
2.多个catch捕捉多个异常 和 一个catch捕捉多个异常
try {
} catch (IndexOutOfBoundsException e) {
System.err.println("IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
}
//这个时候,里面的引用是隐式的final类型,ex不能改指向。
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
3.throws/throw 关键字
二者使用异同?
- throws写在函数签名末尾,表明了这个函数可能会出现的异常,throw在函数中确切地抛出一个异常。
- throws后面接异常类名称,throw后面接异常实例
- throws后面可以接多种异常类名,throw每次只能抛出一个异常。
- 出现异常后,二者都会抛给上一层,让上一层处理。
代码示例:
throw 使用样例:
public class Example1{
void checkAge(int age){
if(age<18)
throw new ArithmeticException("Not Eligible for voting");
else
System.out.println("Eligible for voting");
}
public static void main(String args[]){
Example1 obj = new Example1();
obj.checkAge(13);
System.out.println("End Of Program");
}
}
throws 使用样例:
public void writeList() throws IOException, IndexOutOfBoundsException {}
使用throws的好处:
1.虽然我们可以使用try catch处理异常,但是如果你有多个函数会抛出异常,你得为每个函数都写一个try catch,那样很繁琐,所以我们就将异常统一交给调用外层来处理。
2.当使用throws关键字,你调用该方法编译器会提醒你处理异常,不然报错,这样提升了代码的准确性。
使用总结:
确定会出现异常位置,你可以用throw抛出去。
如果不想自己抛,用throws方法,会帮你把异常抛给函数调用者。
4.finally关键字
从字面上理解,finally是“最后”的意思,在代码中,我们可以理解为“收尾”工作,所谓收尾,就像平时举办活动一样,活动中不管有没有出现异常都会收尾,顺序也肯定是在最后执行。
通常,我们会使用一些streams,connections一些资源接口,在使用完毕后我们要在finally块中收尾显示完成关闭接口。
public static void main(String[] args){
try{
int num[] = {1, 2, 3, 4};
System.out.println(num[5]);
System.out.println("这是异常后的位置");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("异常发生,处理异常中---");
}finally {
System.out.println("异常处理收尾收尾---");
}
System.out.println("异常处理后---");
}
输出:
异常发生,处理异常中---
异常处理收尾收尾---
异常处理后---
一个语法糖:try-with-resources
之前我们讲到finally的显示完成关闭接口,在java7之后采用了一种新的方式,可以自动帮我们完成连接的关闭。
方法:
我们只要在try的括号内创建这个reader的对象就行
try(FileReader fr = new FileReader("file path")) {
// use the resource
} catch () {
// body of catch
}
}
要使用这个语法糖,必须满足如下几个条件:
- 使用的类必须实现了 AutoCloseable 接口;
- 你可以在括号里面实例多个资源对象;
- 当你实例化了多个对象,关闭时的顺序和声明时的顺序相反
- 该resource在try块之前实例化,并且里面的resource引用是隐式的final类型,除此以外并无区别
注意点:
- try后面必定有catch语句或者finally语句中的一个。
- try-catch-finally语句之间不能参杂其他代码,必须是紧跟着。
自定义异常
系统定义的异常太少,不满足你的需求,你也可以自定义异常。但是要注意如下几点:
- 所有自定义异常必须间接或者直接是Throwable类的子类。
- 如果要写一个检查型异常类,继承Exception类。
- 如果写一个运行时异常类,需要继承RuntimeException类。