学习B站动力节点视频:www.bilibili.com/video/BV1a5…
1、异常继承结构
- 所有的 异常(Exception)和 错误(Error)都是可抛出的。都继承了 Throwable 类。
- Error是无法处理的,出现后只有一个结果:JVM终止。
- Exception是可以处理的。
- Exception的分类:
- 所有的RuntimeException的子类:运行时异常/未检查异常(UncheckedException)/非受控异常
- Exception的子类(除RuntimeException之外):编译时异常/检查异常(CheckedException)/受控异常
- 编译时异常和运行时异常区别
- 编译时异常特点:在编译阶段必须提前处理,如果不处理编译器报错。
- 运行时异常特点:在编译阶段可以选择处理,也可以不处理,没有硬性要求。
- 编译时异常一般是由外部环境或外在条件引起的,如网络故障、磁盘空间不足、文件找不到等。作用在于提醒程序员检查本地信息。
- 运行时异常一般是由程序员的错误引起的,并且不需要强制进行异常处理
- 注意:编译时异常并不是在编译阶段发生的异常,所有的异常发生都是在运行阶段的,因为每个异常发生都是会new异常对象的,new异常对象只能在运行阶段完成。那为什么叫做编译时异常呢?这是因为这种异常必须在编译阶段提前预处理,如果不处理编译器报错,因此而得名编译时异常。
2、自定义异常
- 编写异常类继承 Exception(编译时异常)/ RuntimeException(运行时异常)
- 提供一个无参数构造方法,再提供一个带 String msg 参数的构造方法,在构造方法中调用父类的构造方法。
// 自定义异常
public class IllegalAgeException extends Exception{
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
}
// 使用: throw 抛出异常
class Test {
public static void main(String[] args) {
int age = 10;
if (age <= 18) {
throw new IllegalArgumentException("不合法的年龄!");
}
}
}
3、异常处理的两种方式
-
声明异常:类似于推卸责任的处理方式
在方法定义时使用
throws关键字声明异常,告知调用者,调用这个方法可能会出现异常。这种处理方式的态度是:如果出现了异常则会 抛给调用者 来处理。 -
捕捉异常:真正处理捕捉异常
在可能出现异常的代码上使用
try..catch进行捕捉处理。这种处理方式的态度是:把异常抓住。其它方法如果调用这个方法,对于调用者来说是不知道这个异常发生的。因为这个异常被抓住并处理掉了。
-
异常在处理的整个过程中应该是:声明和捕捉联合使用。
如果异常发生后需要调用者来处理的,需要调用者知道的,则采用声明方式。否则采用捕捉。
3.1、声明异常
-
使用
throws关键字 (s表示复数,即后面可以抛出多种异常,抛给调用者)eg:
public void m() throws AException, BException... {} -
如果 AException 和 BException 都继承了 XException,那么也可以这样写:
eg:
public void m() throws XException {} -
调用者在调用m()方法时,编译器会检测到该方法上用 throws 声明了异常,表示可能会抛出异常,编译器会继续检测该异常是否为编译时异常,如果为编译时异常则必须在编译阶段进行处理,如果不处理编译器就会报错。
-
如果所有位置都采用 throws ,包括main方法的处理态度也是throws,如果运行时出现了异常,最终异常是抛给了main方法的调用者(JVM)。JVM的处理方式只是在控制台打印异常信息,然后 JVM则会终止程序的执行。
-
发生异常后,在发生异常的位置上,往下的代码是不会执行的,除非进行了异常的捕捉。
3.2、捕捉异常
- 如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。
try {
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
// 如果出现了A类型的异常,底层会 new AException() 赋值给 e
} catch (AException e) {
// 如果捕捉到 AException类型的异常,在这里处理
} catch (BException e) {
// 如果捕捉到 BException类型的异常,在这里处理
} catch (XException e) {
// 如果捕捉到 XException类型的异常,在这里处理
}
-
catch可以写多个。并且遵循自上而下,从小到大。
-
Java7新特性:catch后面小括号中可以编写多个异常,使用运算符“|”隔开。
try {
} catch (AException | BException e) {
}
4、异常的常用方法
Throwable 的方法:
| 方法名 | 说明 |
|---|---|
| public String getMessage() | 返回此 throwable 的详细信息 |
| public String toString() | 返回抛出的简短描述 |
| public void printStackTrace() | 异常错误信息输出在控制台 |
5、finally 语句块
- finally语句块中的代码是一定会执行的。
- finally语句块不能单独使用,至少需要配合try语句块一起使用:
try ... finallytry ... catch ... finally
- 通常在finally语句块中完成资源的释放
- 资源释放的工作比较重要,如果资源没有释放会一直占用内存。
- 为了保证资源的关闭,也就是说:不管程序是否出现异常,关闭资源的代码一定要保证执行。
- final、finally、finalize分别是什么?
- final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。
- finally是一个关键字,和try一起使用,finally语句块中的代码一定会执行。
- finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
6、异常面试题
- 第一题
public class Demo {
public static void main(String[] args) {
System.out.println(m1());// 2
}
public static int m1() {
String str = null;
try {
int l = str.length(); // NullPointerException
System.out.println(l);
} catch (NullPointerException e) {
return 1;
} finally { // 一定会执行
return 2;
}
}
}
- 第二题
public class Demo {
public static void main(String[] args) {
System.out.println(m1());// 3
}
public static int m1() {
String str = null;
int i = 0;
try {
i++; // i=1
int l = str.length(); // NullPointerException
System.out.println(l);
} catch (NullPointerException e) {
return ++i; // i=2
} finally { // 一定会执行
return ++i; // i=3
}
}
}
- 第三题
public class Demo {
public static void main(String[] args) {
System.out.println(m1());// 2
}
public static int m1() {
String str = null;
int i = 0;
try {
i++; // i=1
int l = str.length(); // NullPointerException
System.out.println(l);
return 1;
} catch (NullPointerException e) {
return ++i; // i=2 临时保存返回值temp = 2
} finally { // 一定会执行
++i; // i=3
System.out.println("i=" + i); // i=3
}
}
}