目录
编辑
一、错误和异常的体系
二、异常
2.1 异常的分类
2.2 异常的处理形式
2.3 如何抛出异常
2.4 捕获处理异常
2.5 finally存在的意义
2.6 异常处理流程总结
2.7 自定义异常类
一、错误和异常的体系
1. Java中所有的异常和错误都有对应的类来进行描述。
2. 由于异常和错误的种类繁多,为了对其进行管理,Java内部维护了一个关于异常和错误的体系(下图为不完整示意图,上下级之间表示继承关系):
① Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception。
② Error:指的是Java虚拟机无法解决的严重错误,比如:JVM的内部错误、资源耗尽等,典型代表: StackOverflowError(栈溢出错误) 和 OutOfMemoryError,一旦发生回力乏术。
③ Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。我们平时所说 的异常就是Exception。
注意:编译时出现的语法性错误,不能称之为异常或错误,例如:将 System.out.println 拼写错了, 写成了 system.out.println,这并不属于Exception 或 Error。
二、异常
2.1 异常的分类
1. 根据异常的发生时机不同,异常可以分为:编译时异常,也称为受检查异常(上图黄色部分) 和 运行时异常,也称为非受检查异常(上图绿色部分)。
2. 编译时异常:例如:在利用Object类中的clone方法实现对象的克隆时,出现的CloneNotSupportedException。
运行时异常:例如:ArrayIndexOutOfBoundsException(数组越界异常)、NullPointerException(空指针异常)、ArithmeticException(算术访问异常)。
2.2 异常的处理形式
1. 错误在代码中是客观存在的,因此我们要让程序出现问题的时候及时通知程序猿,处理异常的两种方式:
① 事前防御型(LBYL: Look Before You Leap):这种做法的缺陷:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱,因此一般不会采取这种写法。
② 事后认错型(EAFP: It's Easier to Ask Forgiveness than Permission):使用try-catch-finally结构,这种做法的优势:正常流程和错误流程是分离开的, 代码更清晰,容易理解代码(示例代码如下图),因此一般会采取这种写法。
try {登陆游戏();开始匹配();游戏确认();选择英雄();载入游戏画面();... } catch (登陆游戏异常) {处理登陆游戏异常; } catch (开始匹配异常) {处理开始匹配异常; } catch (游戏确认异常) {处理游戏确认异常; } catch (选择英雄异常) {处理选择英雄异常; } catch (载入游戏画面异常) {处理载入游戏画面异常; } finally {...... }
2. 在Java中,异常处理主要的5个关键字:try、catch、finally、throw、throws。
2.3 如何抛出异常
1. 处理异常的前提是抛出异常,抛出异常有两种方式:
①程序本身触发异常 ② 程序员手动抛出异常
2. Java中可以借助throw关键字手动抛出一个指定的异常对象,具体语法如下:
throw new xxxException("xxxx");
3. 注意:① throw必须写在方法体内部。
② 抛出的异常对象一定是Exception类 或 RunTimeException的子类。
③ 如果抛出的是RunTimeException 或 RunTimeException的子类对象,程序员可以不处理该异常,直接交给JVM来处理,此时程序会异常返回非零值(如果自己在编译之前就已经发觉了程序可能存在的运行时异常,也可以自己处理)。
④ 如果抛出的是编译时异常,程序员必须自己处理,否则无法通过编译(可以不在所在的方法中处理,此时在所在的方法声明后面加上throws xxx即可,这样操作后可以在调用该方法的方法中再去处理被调用方法存在的异常)。
⑤ 一旦有异常抛出,try中其后面的代码将不会被执行(处理完异常后也不会被执行了)。
⑥ Throwable类的构造方法有带参数的和不带参数的,它的所有子类中也有带参数和不带参数的构造方法,因此在抛出异常对象时可以传参。
2.4 捕获处理异常
1. 在方法中如果出现了编译型异常(此异常不能交给JVM) 或 出现了运行时异常但想自己手动处理,要么直接在所在方法中处理(通过try-catch-finally去处理),要么在所在方法的声明后面加上throws xxxException,意思相当于我先不在本方法中处理这个异常,哪个方法调用这个方法,哪个方法就去负责处理被调用方法中存在的异常(此时也是通过try-catch-finally去处理的),对于编译型异常,如果在调用方法中放任不管仍然会报错,如果在调用方法的声明后面也加上了throws xxxException,则被调用方法中的异常会被交给JVM处理,但JVM不会处理编译型异常,最终会导致程序直接崩溃,异常返回。
2. 语法格式: 修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{ }
3. 注意:① throws必须跟在方法的参数列表之后。
② throws后面声明的异常必须是Exception 或 Exception的子类。
③ 方法内部如果抛出了多个异常,throws之后也必须跟多个异常类型,异常类型之间用逗号隔开,如果多个异常有父子关系,直接声明父类即可。
④ 调用有声明异常的方法时,调用者必须对被调用方法存在的异常进行处理,或者继续使用throws抛出异常。
⑤ throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch-finally。
4. try-catch-finally处理异常:
注意:① try块内抛出异常位置之后的代码将不会被执行,一个try-catch-finall语句只能捕捉和处理一个异常,处理完一个异常完后,try后面的代码也不会被执行了。
② 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的。
③ try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获。这句话是说try中可能会出现多种异常的情况,但具体出现哪种或哪几种在实际开发中并不是都能精准预判到的,例如前端传给后端数据时,你也不知道传来的东西会不会成功引发某种异常,所以你在catch时把try中所有可能会出现的异常都写上,不过注意,try-catch-finally只能处理一次异常,try存在的其他异常将不会去处理,程序执行完finally就跳出当前的try-catch-finally了。
④ 如果多个异常的处理方式是完全相同, 也可以写成这样:
⑤ 如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误。
⑥ 可以通过一个catch捕获所有的异常,即多个异常使用Exception一次捕获(但这种方法不推荐)。
⑦ : catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象.。
2.5 finally存在的意义
1. 在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库 连接、IO流等,在程序正常或者异常退出时,必须要对资源进行回收,另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。(总之咯,如果你使用try-catch-去捕获和处理异常,如果正常捕获或处理了还好说,此时程序会跳出try-catch语句块,执行程序后面的代码(含对资源回收的代码),但如果没有正常捕获到try中具有的异常,那么此时所存在的异常就会被向上传递,一直到 main 方法也没有合适的代码处理异常的话, 就会交给 JVM 来进行处理, 此时程序就会异常终止,因而会导致try-catch-finally后边的代码将不会被执行到,即资源回收的代码将不会被执行到,综上,于是就有了finally存在的意义,我们会把对资源进行回收、有些可能执行不到的语句写在finally里面而不是try-catch-finally外面)
2. 注意:① finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。
② finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally),但是如果 finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return。 一般我们不建议在 finally 中写 return (被编译器当做一个警告)。
2.6 异常处理流程总结
① 程序先执行 try 中的代码。
② 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
③ 如果找到匹配的异常类型, 就会执行 catch 中的代码。
④ 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者。
⑤ 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行)。
⑥ 如果上层调用者也没有处理的了异常, 就继续向上传递。
⑦ 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止。
2.7 自定义异常类
1. 具体方式:① 自定义异常类,然后继承自Exception 或者 RunTimeException
② 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因
2. 注意事项:自定义异常通常会继承自 Exception 或者 RuntimeException,其中继承自 Exception 的异常默认是受查异常,继承自 RuntimeException 的异常默认是非受查异常。
3. 自定义示例:
本篇已完结 ......