=======
当Java 运行时环境接收到异常对象时,如果为该对象寻找catch块呢?
当 Java运行时环境接收到异常对象后,会依次判断该异常对象是否是catch块后异常类或其子类的实例,如果是,Java运行环境将调用该catch块来处理该异常;否则再次拿该异常对象和下一个catch块里的异常类进行比较。Java 异常捕获如下图:
try 块后可以有多个catch块,这是为了针对不同的异常类提供不同的异常处理方式。当系统发生不同的意外情况时,系统会生成不同的异常对象,Java运行时就会根据该异常对象所属的异常类来决定使用哪个catch块来处理该异常。
try代码块
======
-
try : 监视代码执行过程,一旦发现异常则直接跳转 值catch,如果没有catch,则直接跳转至finally
-
catch : 可选执行的代码块 ,如果没有任何异常发生则不会执行;如果发现异常则进行处理或者向上抛出。这一切都在catch代码 快照红执行
-
finally: 必选执行的代码块,不管是否有异常产生,即使发生OutofMemoryError也会执行,通常用于善后清理工作。
如果finally代码块没有执行,有三种可能
1.没有进入try代码块
2.进入 try代码块,但是代码运行中出现了死循环或者死锁状态
3.进入try代码块,但是执行了Sytem.exit()操作
注意:finally 是在return 表达式运行后执行的,此时将要return的结果已经被暂存起来,待finally代码块执行结束后再将之前暂存的结果返回。示例代码如下:
public static int test(){
int temp = 10000;
try{
throw new Exception(“”);
}catch (Exception e){
return ++temp;
}finally {
temp = 9999;
}
}
我们从字节码操作来看返回的结果:
此方法的返回值是 10001而不是9999;
0 sipush 10000
3 istore_0 # 将栈顶int类型值保存到局部变量0中。 将 10000存在 slot_0上
4 new #5 <java/lang/Exception>
7 dup
8 ldc #6
10 invokespecial #7 <java/lang/Exception. : (Ljava/lang/String;)V>
13 athrow
14 astore_1 #将栈顶引用类型值保存到局部变量1中。
15 iinc 0 by 1 # 对变量temp 进行 + 1 操作
18 iload_0 # 从局部变量0中装载int类型值入栈。
19 istore_2 # 将栈顶int类型值保存到局部变量2中。
20 sipush 9999 # 将9999存储到slot_0上
23 istore_0
24 iload_2 #方法返回值的时候,直接提取的是 slot_2的值,即10001
25 ireturn
26 astore_3
27 sipush 9999
30 istore_0
31 aload_3
32 athrow
finally的职责:不是对变量进行赋值等操作,而是清理资源、释放连接、关闭管道流等操作。
finally 代码块中使用return语句,使返回值的判断更加复杂,所以避免返回值不可控,我们不要在finally代码块中使用 return 语句。
try代码块与锁的关系,lock方法可能会抛出unchecked异常,如果挡在try代码块中,必然触发finally 中的unlock 方法执行。对未加锁的对象解锁会抛出unchecked异常,如
IllegalMonitorStateExecption ,虽然是因为加锁失败而造成程序中断的,但是真正的加锁失败的原因可能会被后者覆盖。所以在try代码块之前调用lock()方法,避免由于加锁失败导致 finally 调用unlock() 抛出异常。代码如下
Lock lock = new xxxLock();
try{
// 无论加锁是否成功,unlock 都会执行
lock.lock();
doSomething();
}finally{
lock.unlock();
}
Lock、ThreadLocal、InputStream等这些需要进行强制释放和清除的对象都得在finally代码块中进行显示的清理,避免产生内存泄漏或者资源消耗
那么为什么finally代码块为什么一定会执行呢
我们看下面代码的字节码分析
public void foo() {
try {
tryItOut1();
} catch (MyException1 e) {
handleException(e);
} finally {
handleFinally();
}
}
对应的字节码如下:
可以看到,字节码中包含了三份 finally 语句块,都在程序正常 return 和异常 throw 之前。其中两处在 try 和 catch 调用 return 之前,一处是在异常 throw 之前。
Java 采用方式是复制 finally 代码块的内容,分别放在 try catch 代码块所有正常 return 和 异常 throw 之前。
相当于如下的代码:
public void foo() {
try {
tryItOut1();
handleFinally();
} catch (MyException1 e) {
handleException(e);
handleFinally();
} catch (Throwable e) {
handleFinally();
throw e;
}
}
异常的处理思路
=======
处理异常程序,需要解决一下三个问题:
(1) 哪里发生异常