一、概念
a) 异常 :程序运行过程中出现的非正常的情况。 b) 异常处理 :当异常出现时,执行预先准备好的程序。 c) 异常处理的必要性:减少用户损失,提升用户体验;捕获异常后可以给用户友好提示,避免程序直接崩溃。
二、异常的分类
Throwable:Java 语言中异常或者错误的父类,位于java.lang包。
i. Throwable 构造方法
Throwable():无参数构造方法Throwable(String message):携带异常信息的构造方法
ii. Throwable 成员方法
String getMessage():获取字符串类型异常信息void printStackTrace():打印栈追踪信息,查看异常发生完整调用链路
b) Error
Error是Throwable的子类,代表错误。
注意:Error 一般是程序本身无法恢复的严重错误,例如 JVM 内存不足、栈溢出
java.lang.StackOverflowError。 开发中一般不处理 Error。
c) Exception
Exception是Throwable的子类,代表异常,也是我们开发中重点处理的对象。
i. RuntimeException:运行时异常(非检查异常)
直接 / 间接继承RuntimeException,编译器不会检查,代码编译可以通过,运行时才会报错。
特点:可处理,也可以不处理;大多可以通过严谨代码规避。
常见运行时异常:
ArrayIndexOutOfBoundsException数组下标越界IndexOutOfBoundsException集合下标越界StringIndexOutOfBoundsException字符串下标越界NullPointerException空指针异常ClassCastException类型转换异常NumberFormatException数字格式转换异常
ii. 非 RuntimeException:非运行时异常(受检异常 / 编译异常)
和RuntimeException没有继承关系,编译器会强制检查。
特点:代码编译阶段就会报错,必须处理 ,否则无法编译通过。 示例:
Class.forName("String");
iii. 异常产生的两种方式
- 自动产生异常
- 原因:程序运行时执行错误代码,JVM 自动抛出异常
- 结果:一旦异常抛出,后续代码不再执行,程序终止。
- 手动产生异常
- 关键字:
throw - 语法:
throw new 异常类名("异常信息"); - 位置:写在方法内部
- 效果:类似
return,终止当前方法,抛出异常。
- 关键字:
iv. 异常的传递
Java 中的异常沿着方法调用链反向传递,如果全程没有捕获处理,最终会传递到 JVM,由 JVM 终止程序。
三、异常的处理
a) 消极处理异常:声明异常 throws
i. 关键字:throws ii. 位置:写在方法声明之后 iii. 语法
修饰符 返回值类型 方法名(形参列表) throws 异常类名1,异常类名2{
//方法体
}
iv. 注意:声明异常时,可以直接声明父类异常,代表抛出该父类下所有子类异常。 v. 作用:仅仅把异常责任向外抛出,推卸异常处理责任,最终交给调用者,如果调用者也不处理,异常最终给到 JVM,程序依旧会终止。
作用:让受检异常编译通过。
b) 积极处理异常:try-catch 捕获异常
i. 基础语法
try{
//可能出现异常的代码
}catch(异常类名 e){
//异常发生后执行的代码
}
ii. 规则说明
- 只有
try内部发生异常,catch块才会执行;无异常时,catch不执行。 - 一个
try后面可以跟多个catch;发生异常时,从上往下依次匹配,匹配成功进入对应 catch。 - catch 可以写父类异常,利用多态捕获本类及其所有子类异常。
- 子类异常 catch 必须写在父类异常 catch 前面,否则编译报错。
- catch 块中可以捕获 try 代码里可能出现的受检异常。
- catch 块可以写任意运行时异常,哪怕 try 中不会出现该异常。
iii. finally:必须执行的语句块
-
语法
try{
//可能出现异常
}catch(异常类 e){
//异常处理
}finally{
//无论是否发生异常,都会执行的代码
}
注意:开发中不建议在 finally 内部写 return;finally 典型用途:资源关闭,例如 IO 流、数据库连接等。
- 常用组合结构小结
try{}catch{}try{}catch{}catch{}try{}catch{}finally{}try{}catch{}catch{}finally{}try{}finally{}:不捕获异常,但是保证 finally 代码一定执行- 嵌套形式:
try{ try{}catch{} } catch{}、try{}catch{ try{}catch{} }、try{}catch{}finally{ try{}catch{} }
注意:finally 不能单独使用,必须配合 try 或者 try-catch。
配套代码示例(可直接运行)
public class ExceptionDemo {
public static void main(String[] args) {
try {
int[] arr = {1,2,3};
System.out.println(arr[5]); //数组越界,抛出异常
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获到数组下标越界异常");
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
System.out.println("finally块执行,用于关闭资源");
}
}
}
throws 声明异常示例
public class ThrowsDemo {
// 消极抛出异常,交给调用者处理
public static void test() throws ClassNotFoundException {
Class.forName("com.test.Demo");
}
public static void main(String[] args) {
try {
test();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
throw 手动抛出异常示例
public class ThrowDemo {
public static void checkAge(int age){
if(age < 0){
throw new RuntimeException("年龄不能为负数!");
}
System.out.println("年龄合法");
}
public static void main(String[] args) {
checkAge(-5);
}
}
面试小结
Error和Exception区别:Error 是严重错误,程序无法处理;Exception 是异常,可以捕获处理。- 运行时异常和受检异常区别:受检异常编译期强制处理;运行时异常编译不检查。
throws和throw区别:throws在方法声明,声明抛出异常;throw在方法内部,手动抛出异常对象。- finally 一定会执行吗?
大部分情况都会执行;唯一不执行场景:
try内部执行System.exit(0)退出 JVM,finally 不会执行。