文章目录
在Java中,程序出现的某些不正常行为,很难通过修改代码来解决的一些问题就是异常
注意:异常名实际上是类!!!
一、异常的体系结构
体系结构如下图:
箭头指向为被继承的类,与前面学习的类与类之间的继承相同,接下来对上图的一些困惑进行解释:
Throwable:是异常体系的顶层类,派生出两个重要的子类,Error和ExceptionError:指的是Java虚拟机无法解决的严重问题Exception:异常后程序员可以通过代码处理,使得程序继续执行
二、异常的分类
1.编译时异常
在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)
2.运行时异常
在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)
RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
注意:如果说编写代码时的语法错误,并不能称之为异常!
三、异常的处理
1.在有异常时通知到位(防御式编程)
在这种状况下,有两种处理方式:
- 事前防御性:先预计可能出现的问题,如果真的出现了预料中的问题再进行处理
- 事后认错型:等到出现问题了再进行处理
这就是处理异常的两个不同的思路,在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。
接下来正式介绍异常应该如何处理
2.异常的抛出
抛出的意思就是如果出现了问题,你就要把这个问题告知给程序员,让其找到并修改.
具体语法如下:
throw new XXXException("异常产生的原因");
使用方法为:
public static int getElement(int[] array, int index){
if(null == array){
throw new NullPointerException("传递的数组为null");
}
if(index <
0 || index >
= array.length){
throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {
1,2,3
};
getElement(array, 3);
}
注意事项为:
throw必须写在方法体内部- 抛出的对象必须是
Exception或Exception的子类对象 - 如果抛出的是
RunTimeException或者RunTimeException的子类对象,则可以不用处理,交给JVM来处理 - 如果抛出的是编译时异常,用户必须处理,否则程序无法通过编译
- 当异常抛出后,其后的代码便不会再执行
3.异常的捕获
A.异常声明throws
(如果用户不想处理该异常,提醒方法的调用者处理异常)
语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
- throws必须跟在方法的参数列表之后
- 声明的异常必须是Exception或者其子类
- 方法内部如果抛出了多个异常,throws之后必须跟上多个异常类型,用逗号隔开,如果他们之间有父子关系,仅仅声明父类即可
B.核心——try-catch捕获并处理
语法格式:
try{
//将可能要出现异常的代码放在这里
}catch(要捕获的异常类型 e){
//如果try中的代码抛出异常了,此处catch捕获时异常类型与try抛出的异常类型一致时,或者是try中抛出的异常基类时,就会被捕捉到
//处理完异常后,跳出try-catch,继续执行
}fianlly{
//此处代码一定执行
}
那如果是抛出多个异常对象,还是这样吗?
当然,仍然是,只不过是多种异常,多次捕获,catch可以写成这样:
catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
//...
}
如图,可以用|来连接
那如果异常之间有父子关系的话,顺序有什么要求呢?
一定是子类在前catch,父类在后,否则会发生语法错误
4.异常的处理流程
方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述. 在 JVM 中有一块内存空间称为"虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.
如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递
如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try-catch 时是一样的)
异常处理流程总结:
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
四、自定义异常类
具体方法:
- 自定义异常类,然后继承自Exception 或者 RunTimeException
- 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因
示例代码:
class UserNameException
extends Exception {
public UserNameException(String message) {
super(message);
}
}
class PasswordException
extends Exception {
public PasswordException(String message) {
super(message);
}
}
注意事项:
- 自定义异常通常会继承自Exception或是RunTimeException
- 继承自Exception的异常通常是受查异常(必须被处理的异常)
- 继承自RunTimeException的异常通常是非受查异常
文章如有明显错误欢迎大家指正.
浙公网安备 33010602011771号