一、基本概念
异常就是程序执行时不正常的现象
异常的出现是为了提升程序健壮性
java 中异常以类的形式出现
异常的继承关系图,Exception 是所有异常的基类。(Error 指错误)
Exception 和 Error 的区别:
- Exception是 java 程序运行中
可预料
的异常,可以人为干预抛出 和 捕捉; - Error 指
不可预料
的错误,会直接导致 JVM 不可处理或不可恢复所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。
一、异常分类
注意:异常的产生是 new 出异常对象,所以异常是运行时产生的。
编译时异常
指必须在编写程序时预先处理的异常,如果不处理编译器会报错
编译时异常发生的概率比较高
因为需要预先处理,也称『受检异常』、『受控异常』
运行时异常
在编写程序时非必须处理异常,可以处理也可以不处理
运行时异常发生的概率比较低
因为不需要预先处理,也称『未受检异常』、『非受控异常』
三、异常的处理方式
以下面这段代码举例:
- 当程序非正常执行时,
100/i
这行代码会抛出 ArithmeticException 异常(该异常属于运行时异常) - 执行这行代码的 main 方法接收异常,main 方法没有对异常进行处理,继续上抛至 jvm
- jvm 终止程序
public class ExceptionTest {
public static void main(String[] args) {
int i = 0;
System.out.println( 100/i);
}
}
扑获异常 or 抛出
如需上层处理异常时,则可以将异常抛出,其他情况需要处理异常
- try 包裹可能抛出异常的代码块, catch
捕获异常
,并处理(调用者不感知异常
) - finally 异常处理时一定会被执行的代码,一般用来资源释放等等
- throw 主动
抛出异常
- throws 声明异常,上报给调用者,表示可能要抛出该异常(
调用者感知异常
)
public class ExceptionTest {
public static void main(String[] args) throws NullPointerException{
try {
test1();
} catch (ArithmeticException e) {
// e引用 保存的内存地址是那个new出来 异常对象的内存地址
System.out.println(" 捕捉 ArithmeticException 异常");
}finally {
// finally 语句中的代码一定会被执行
System.out.println("finally 语句中的代码一定被执行");
}
// throw 关键字用来抛出 异常对象
throw new NullPointerException("主动抛出 空指针 异常");
// 下面程序不处理 NullPointerException 而是直接上抛
}
// throws 关键字只是声明异常,表示可能要抛出该异常
public static void test1() throws ArithmeticException {
int i = 0;
System.out.println(100 / i);
}
}
深入 try catch
- catch 后面可以是具体的异常类,也可以是该异常类型的父类型(多态)
- catch 可以有多个,也可以同时捕捉多个异常,建议分多个捕捉,便于调试
- catch 分多个写的时候需要由细到粗
深入 finally
1、finally 必须和 try 搭配使用(try catch --> finally)\(try --> finally)
2、finally 失效场景,执行前调用 System.exit(0);
退出JVM之后,finally语句中的代码就不执行了
3、finally 代码为什么一定会被执行?
- 在程序执行中,不一定会抛出异常时,会将 finally 的代码拷贝到 try 模块的最后,也会拷贝到 catch 模块的最后来确保代码一定会被执行。
- 在程序执行中,如果一定会抛出异常,则执行顺序是(try --> catch --> finally)\(try --> finally)。
- 如何理解:这里 finally 相当于一个 catch ,他捕获 (try catch) 块中所有的异常(catch 中捕获 try 块中的所有异常)。对于一定会异常的块中,则一定会在 catch 中捕获,所以不需要将代码进行拷贝。
举例一:返回 i= 1
public static int test2() {
int i = 0;
try{
i = 1;
// 会将 finally 模块的 i = 2;复制过来执行,但是执行只影响 finally 模块局部变量的值
// i = 2;
// 此处返回的 try 模块的局部变量 i = 1
return i;
}finally {
// 此处对 finally 模块的局部变量 i 进行赋值为 2
i = 2;
}
// 该行代码不会被执行
// System.out.println(100 / i);
}
4、finally 代码为什么不能 return ?
因为上面 finally 的代码一定会被执行的原因,可能导致 代码覆盖。
举例:i=2
public static int test2() {
int i = 0;
try{
i = 1;
// 会将 finally 模块的代码复制过来执行,执行影响 finally 局部变量的值
// 但是由于有 finally 中有 return,最后返回的的 finally 局部变量的值, 将 try 模块中的计算结果丢掉了
// i++;
// return i;
return i;
}finally {
// 此处对 finally 模块的 局部变量 i 进行赋值为 2
i = 2;
return i;
}
// 该行代码不会被执行
// System.out.println(100 / i);
}
java 继承方法重写与异常
子类重写父类方法时如果要抛出异常,只能抛出比父类异常更宽泛的异常。
这个原因 参考下大佬的文章
我自己实践了一下,没有明白按照这个多态的原理,为什么父类没有抛出异常时,子类可以抛出异常。????