目录
什么是异常
程序在运行时出现错误时通知调用者的一种机制(已经编译得到了class文件,再由JVM执行过程中出现的错误)。
异常处理主要的5个关键字
:throw、try、catch、final、throws。
异常的体系结构
1. Throwable : 是异常体系的顶层类,其派生出两个重要的子类 , Error 和 Exception2. Error : 指的是 Java 虚拟机无法解决的严重问题,比如: JVM 的内部错误、资源耗尽等 ,典型代表:StackOverflflowError 和 OutOfMemoryError ,一旦发生回力乏术。3. Exception : 异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说 的异常就是Exception 。
异常语法
语法格式: try{ // 可能会发生异常的代码 }catch(异常类型 e){ // 对捕获到的异常进行处理 }finally{ // 此处的语句无论是否发生异常,都会被执行到 } // 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
当出现异常时,异常后面的代码不会被执行
当程序抛出异常,由catch块进行捕获,程序自己来处理异常,此时,程序会继续向下执行。
异常的分类
异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同 。
1.运行时异常---非受查异常
2.编译时异常---受查异常
异常的处理流程
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, fifinally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止。
关于 " 调用栈 "方法之间是存在相互调用关系的, 这种调用关系我们可以用 "调用栈" 来描述. 在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace() ; 的 方式查看出现异常代码的调用栈. 如果向上一直传递都没有合适的方法处理异常 , 最终就会交给 JVM 处理 , 程序就会异常终止 ( 和我们最开始未使用 try catch 时是一样的 ).
异常的处理
防御式编程
LBYL:操作前做好准备。Look Before You Leap.
EAFP:先操作遇到问题再处理。It's Easier to Ask Forgiveness than Permission.
异常处理的核心思想就是 EAFP。
异常的抛出
在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。在 Java 中,可以借助 throw 关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:throw new XXXException("异常产生的原因"); eg: public static int getElement(int[] array, int index){ if(null == array){ throw new NullPointerException("传递的数组为null"); if(index < 0 || index >= array.length){ throw new ArrayIndexOutOfBoundsException("传递的数组下标越界"); }
throw的注意事项
1. throw必须写在方法体内部;
2. 抛出的对象必须是Exception 或者 Exception 的子类对象;
3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理;
4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译;
5. 异常一旦抛出,其后的代码就不会执行;
异常的捕获
异常的捕获,也就是异常的具体处理方式,主要有两种: 异常声明throws 以及 try-catch捕获处理 。
异常声明throws
当前方法不处理异常,提醒方法的调用者处理异常。
语法格式: 修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{ }
【 注意事项 】1. throws 必须跟在方法的参数列表之后2. 声明的异常必须是 Exception 或者 Exception 的子类3. 方法内部如果抛出了多个异常, throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型 ,具有父子关系,直接声明父类即可。4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用 throws 抛出
try-catch捕获处理
throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行 处理,就需要try-catch。
需求:读取配置文件,如果配置文件名字不是指定名字,抛出异常,调用者进行异常处理
注意:
🍒1. try 块内抛出异常位置之后的代码将不会被执行;🍒2. 如果抛出异常类型与 catch 时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序 ---- 异常是按照类型来捕获的。🍒3.如果异常之间具有父子关系,一定是子类异常在前 catch ,父类异常在后 catch;4. 可以通过一个 catch 捕获所有的异常,即多个异常,一次捕获 ( 不推荐 )由于 Exception 类是所有异常类的父类 . 因此可以用这个类型表示捕捉所有异常 .备注 : catch 进行类型匹配的时候 , 不光会匹配相同类型的异常对象 , 也会捕捉目标异常类型的子类对象 .
finally
在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库 连接、IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能 导致有些语句执行不到 , fifinally 就是用来解决这个问题的。
语法格式:
try{
可能会发生异常的代码
}catch(异常类型 e){
对捕获到的异常进行处理
}finally{
此处的语句无论是否发生异常,都会被执行到
}
如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
既然finally 和 try-catch-finally 后的代码都会执行,那为什么还要有finally呢?
避免资源的泄露
finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果 finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.一般我们不建议在 finally 中写 return ( 被编译器当做一个警告 ).
自定义异常类
1. 自定义异常类,然后继承自 Exception 或者 RunTimeException2. 实现一个带有 String 类型参数的构造方法,参数含义:出现异常的原因3. 继承自 Exception 的异常默认是受查异常4. 继承自 RuntimeException 的异常默认是非受查异常
throw和throws区别
throws
和throw
是 Java 中的关键字,但它们的用法是不同的。1、throw代表动作,表示抛出一个异常的动作; throws代表一种状态,代表方法可能有异常抛出。
2、throw用在方法实现中,而throws用在方法声明中。
3、throw只能用于抛出一种异常,后面跟着一个异常对象。而throws可以抛出多个异常,但不会进行异常处理。